From 337b65512aef2f5b7964d39da5ef5b183a337c05 Mon Sep 17 00:00:00 2001 From: A Date: Sun, 13 Nov 2022 11:53:51 +0100 Subject: [PATCH 01/55] Created new integration tests --- integration/integration_test.go | 818 +++++++++++++++++++++++++++++++- 1 file changed, 812 insertions(+), 6 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index f6b7b4409..f67ca2914 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -44,7 +44,29 @@ var serdecontext = json.NewContext() // Check the shuffled votes versus the cast votes on a few nodes func TestIntegration(t *testing.T) { t.Run("3 nodes, 3 votes", getIntegrationTest(3, 3)) - t.Run("10 nodes, 10 votes", getIntegrationTest(10, 10)) + t.Run("10 nodes, 100 votes", getIntegrationTest(10, 100)) +} + +func TestBadVote(t *testing.T) { + t.Run("5 nodes, 10 votes including 5 bad votes", getIntegrationTestBadVote(5, 10, 5)) +} + +func TestRevote(t *testing.T) { + t.Run("5 nodes, 10 votes ", getIntegrationTestRevote(5, 10)) +} + +func TestCrash(t *testing.T) { + t.Run("5 nodes, 5 votes, 1 fail", getIntegrationTestCrash(5, 5, 1)) + t.Run("5 nodes, 5 votes, 2 fails", getIntegrationTestCrash(5, 5, 2)) + t.Run("10 nodes, 5 votes, 3 fails", getIntegrationTestCrash(10, 5, 3)) +} + +func TestAddNodes(t *testing.T) { + t.Run("5 nodes, 10 votes ", getIntegrationTestAddNodes(5, 10, 2)) +} + +func BenchmarkIntegration(b *testing.B) { + b.Run("10 nodes, 100 votes", getIntegrationBenchmark(10, 100)) } func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { @@ -67,7 +89,597 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { t.Logf("using temp dir %s", dirPath) // ##### CREATE NODES ##### - nodes := setupDVotingNodes(t, numNodes, dirPath) + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) + + signer := createDVotingAccess(t, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(t, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(t, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(t, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(t, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + t.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + castedVotes, err := castVotesRandomly(m, actor, form, numVotes) + require.NoError(t, err) + + fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(t, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(t, err) + + // ##### SHUFFLE BALLOTS ##### + t.Logf("initializing shuffle") + sActor, err := initShuffle(nodes) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + t.Logf("shuffling") + err = sActor.Shuffle(formID) + require.NoError(t, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### SUBMIT PUBLIC SHARES ##### + t.Logf("submitting public shares") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + err = actor.ComputePubshares() + require.NoError(t, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### DECRYPT BALLOTS ##### + t.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + t.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(t, err) + err = decryptBallots(m, actor, form) + require.NoError(t, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(t, err) + + t.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + require.Len(t, form.DecryptedBallots, len(castedVotes)) + + for _, b := range form.DecryptedBallots { + ok := false + for i, casted := range castedVotes { + if b.Equal(casted) { + //remove the casted vote from the list + castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) + ok = true + break + } + } + require.True(t, ok) + } + require.Empty(t, castedVotes) + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(t, err) + + fmt.Println("test done") + } +} + +func getIntegrationTestBadVote(numNodes, numVotes, numBadVotes int) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(t, err) + + defer os.RemoveAll(dirPath) + + t.Logf("using temp dir %s", dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) + + signer := createDVotingAccess(t, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(t, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(t, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(t, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(t, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + t.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + //cast a vote with wrong answers: Should not be taken into account + + _, err = castVotesRandomly(m, actor, form, numVotes-numBadVotes) + require.NoError(t, err) + + err = castBadVote(m, actor, form, numBadVotes) + require.NoError(t, err) + + //castedVotes = append(castedVotes, badVotes...) + + //fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(t, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(t, err) + + // ##### SHUFFLE BALLOTS ##### + t.Logf("initializing shuffle") + sActor, err := initShuffle(nodes) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + t.Logf("shuffling") + err = sActor.Shuffle(formID) + require.NoError(t, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### SUBMIT PUBLIC SHARES ##### + t.Logf("submitting public shares") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + err = actor.ComputePubshares() + require.NoError(t, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### DECRYPT BALLOTS ##### + t.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + t.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(t, err) + err = decryptBallots(m, actor, form) + require.NoError(t, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(t, err) + + t.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + //require.Len(t, form.DecryptedBallots, numVotes-numBadVotes) + //should contains numBadVotes empty ballots + count := 0 + for _, ballot := range form.DecryptedBallots { + if ballotIsNull(ballot) { + count++ + } + fmt.Println(fmt.Sprintf("%#v", ballot)) + } + fmt.Println(form.DecryptedBallots) + + require.Equal(t, numBadVotes, count) + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(t, err) + + fmt.Println("test done") + } +} + +func getIntegrationTestRevote(numNodes, numVotes int) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(t, err) + + defer os.RemoveAll(dirPath) + + t.Logf("using temp dir %s", dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) + + signer := createDVotingAccess(t, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(t, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(t, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(t, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(t, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + t.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + _, err = castVotesRandomly(m, actor, form, numVotes) + require.NoError(t, err) + + castedVotes, err := castVotesRandomly(m, actor, form, numVotes) + require.NoError(t, err) + + fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(t, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(t, err) + + // ##### SHUFFLE BALLOTS ##### + t.Logf("initializing shuffle") + sActor, err := initShuffle(nodes) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + t.Logf("shuffling") + err = sActor.Shuffle(formID) + require.NoError(t, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### SUBMIT PUBLIC SHARES ##### + t.Logf("submitting public shares") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + err = actor.ComputePubshares() + require.NoError(t, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### DECRYPT BALLOTS ##### + t.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + t.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(t, err) + err = decryptBallots(m, actor, form) + require.NoError(t, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(t, err) + + t.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + require.Len(t, form.DecryptedBallots, len(castedVotes)) + + for _, b := range form.DecryptedBallots { + ok := false + for i, casted := range castedVotes { + if b.Equal(casted) { + ok = true + //remove the casted vote from the list + castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) + break + } + } + require.True(t, ok) + } + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(t, err) + + fmt.Println("test done") + } +} + +func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(t, err) + + defer os.RemoveAll(dirPath) + + t.Logf("using temp dir %s", dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) + + signer := createDVotingAccess(t, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(t, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(t, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(t, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(t, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + t.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + // crashNodeList nodes crashes during the process + + var crashNodeList []dVotingCosiDela + for i := 0; i < failingNodes; i++ { + crashID := rand.Intn(numNodes - i) + crashNode := nodes[crashID] + nodes = append(nodes[:crashID], nodes[crashID+1:]...) + crashNodeList = append(crashNodeList, crashNode) + } + err = closeNodes(crashNodeList) + require.NoError(t, err) + + castedVotes, err := castVotesRandomly(m, actor, form, numVotes) + require.NoError(t, err) + + fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(t, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(t, err) + + // ##### SHUFFLE BALLOTS ##### + t.Logf("initializing shuffle") + sActor, err := initShuffle(nodes) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + t.Logf("shuffling") + err = sActor.Shuffle(formID) + + // If the number of failing nodes is greater than the threshold, the shuffle will fail + fmt.Println("threshold: ", numNodes/3) + if failingNodes > numNodes/3 { + require.Error(t, err) + return + } + + require.NoError(t, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### SUBMIT PUBLIC SHARES ##### + t.Logf("submitting public shares") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + err = actor.ComputePubshares() + require.NoError(t, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### DECRYPT BALLOTS ##### + t.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + t.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(t, err) + err = decryptBallots(m, actor, form) + require.NoError(t, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(t, err) + + t.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + require.Len(t, form.DecryptedBallots, len(castedVotes)) + + for _, b := range form.DecryptedBallots { + ok := false + for i, casted := range castedVotes { + if b.Equal(casted) { + //remove the casted vote from the list + castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) + ok = true + break + } + } + require.True(t, ok) + } + require.Empty(t, castedVotes) + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(t, err) + + fmt.Println("test done") + } +} + +func getIntegrationTestAddNodes(numNodes, numVotes, numNewNodes int) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(t, err) + + defer os.RemoveAll(dirPath) + + t.Logf("using temp dir %s", dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) signer := createDVotingAccess(t, nodes, dirPath) @@ -101,6 +713,9 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { form, err := getForm(formFac, formID, nodes[0].GetOrdering()) require.NoError(t, err) + newNodes := setupDVotingNodes(t, numNewNodes, dirPath, nodes[0]) + nodes = append(nodes, newNodes...) + castedVotes, err := castVotesRandomly(m, actor, form, numVotes) require.NoError(t, err) @@ -167,14 +782,17 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { for _, b := range form.DecryptedBallots { ok := false - for _, casted := range castedVotes { + for i, casted := range castedVotes { if b.Equal(casted) { + //remove the casted vote from the list + castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) ok = true break } } require.True(t, ok) } + require.Empty(t, castedVotes) fmt.Println("closing nodes") @@ -185,8 +803,147 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { } } +func getIntegrationBenchmark(numNodes, numVotes int) func(*testing.B) { + return func(b *testing.B) { + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(b, err) + + defer os.RemoveAll(dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(b, numNodes, dirPath,nil) + + signer := createDVotingAccess(b, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(b, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(b, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(b, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(b, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(b, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + b.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(b, err) + + castedVotes, err := castVotesRandomly(m, actor, form, numVotes) + require.NoError(b, err) + + fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(b, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(b, err) + + // ##### SHUFFLE BALLOTS ##### + sActor, err := initShuffle(nodes) + require.NoError(b, err) + + time.Sleep(time.Second * 1) + + err = sActor.Shuffle(formID) + require.NoError(b, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(b, err) + + // ##### SUBMIT PUBLIC SHARES ##### + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(b, err) + err = actor.ComputePubshares() + require.NoError(b, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(b, err) + + // ##### DECRYPT BALLOTS ##### + b.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + b.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(b, err) + err = decryptBallots(m, actor, form) + require.NoError(b, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(b, err) + + b.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(b, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + require.Len(b, form.DecryptedBallots, len(castedVotes)) + + for _, ballot := range form.DecryptedBallots { + ok := false + for _, casted := range castedVotes { + if ballot.Equal(casted) { + ok = true + break + } + } + require.True(b, ok) + } + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(b, err) + + fmt.Println("test done") + } +} + // ----------------------------------------------------------------------------- // Utility functions + +func ballotIsNull(ballot types.Ballot) bool { + return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && + ballot.RankResultIDs == nil && ballot.RankResult == nil && + ballot.TextResultIDs == nil && ballot.TextResult == nil +} + func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, timeout time.Duration, retry int) txManager { @@ -395,8 +1152,8 @@ func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, castVote := types.CastVote{ FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, + UserID: userID, + Ballot: ciphervote, } data, err := castVote.Serialize(serdecontext) @@ -427,6 +1184,55 @@ func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, return votes, nil } +func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":1,0,1,1\n" + + "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" + string("select:" + encodeID("bb") + ":1,1,1,1\n" + + "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" + + } + + for i := 0; i < numberOfBadVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "badUser " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + //votes[i] = ballot + } + + return nil +} + func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { var ballot = make([]types.EGPair, chunks) @@ -459,7 +1265,7 @@ func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervo func closeForm(m txManager, formID []byte, admin string) error { closeForm := &types.CloseForm{ FormID: hex.EncodeToString(formID), - UserID: admin, + UserID: admin, } data, err := closeForm.Serialize(serdecontext) From edd35730f0194d60fb8804b1dea0182a9d6c5310 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 14 Nov 2022 10:27:28 +0100 Subject: [PATCH 02/55] setup possible to previously created node --- integration/dvotingdela.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/integration/dvotingdela.go b/integration/dvotingdela.go index 57383d674..cab73632e 100644 --- a/integration/dvotingdela.go +++ b/integration/dvotingdela.go @@ -109,7 +109,7 @@ type dVotingNode struct { // Creates n dela nodes using tempDir as root to file path and returns an array // of nodes or error -func setupDVotingNodes(t require.TestingT, numberOfNodes int, tempDir string) []dVotingCosiDela { +func setupDVotingNodes(t require.TestingT, numberOfNodes int, tempDir string, node dVotingCosiDela) []dVotingCosiDela { wait := sync.WaitGroup{} @@ -137,7 +137,11 @@ func setupDVotingNodes(t require.TestingT, numberOfNodes int, tempDir string) [] delaNodes = append(delaNodes, node) dVotingNodes = append(dVotingNodes, node) } - delaNodes[0].Setup(delaNodes[1:]...) + if node != nil { + node.Setup(delaNodes...) + } else { + delaNodes[0].Setup(delaNodes[1:]...) + } return dVotingNodes } From 88e55a94dba280b6f1b803b1a01454c3722a736f Mon Sep 17 00:00:00 2001 From: A Date: Mon, 14 Nov 2022 10:32:15 +0100 Subject: [PATCH 03/55] fixed performance test --- integration/performance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/performance_test.go b/integration/performance_test.go index 21b438423..5b986a5c2 100644 --- a/integration/performance_test.go +++ b/integration/performance_test.go @@ -46,7 +46,7 @@ func BenchmarkIntegration_CustomVotesScenario(b *testing.B) { b.Logf("using temp dir %s", dirPath) // ##### CREATE NODES ##### - nodes := setupDVotingNodes(b, numNodes, dirPath) + nodes := setupDVotingNodes(b, numNodes, dirPath,nil) signer := createDVotingAccess(b, nodes, dirPath) From 6fc8992d7077ff5e146d8c6c59ef66a7314e90ec Mon Sep 17 00:00:00 2001 From: A Date: Mon, 14 Nov 2022 10:38:52 +0100 Subject: [PATCH 04/55] removed bad print statment --- integration/integration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index f67ca2914..9d640b422 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -62,7 +62,7 @@ func TestCrash(t *testing.T) { } func TestAddNodes(t *testing.T) { - t.Run("5 nodes, 10 votes ", getIntegrationTestAddNodes(5, 10, 2)) + t.Run("5 nodes, 10 votes add 2 node during the process", getIntegrationTestAddNodes(5, 10, 2)) } func BenchmarkIntegration(b *testing.B) { @@ -340,7 +340,7 @@ func getIntegrationTestBadVote(numNodes, numVotes, numBadVotes int) func(*testin if ballotIsNull(ballot) { count++ } - fmt.Println(fmt.Sprintf("%#v", ballot)) + //fmt.Println(fmt.Sprintf("%#v", ballot)) } fmt.Println(form.DecryptedBallots) From 9242a4afecfd9ad8fa2723fb96c779f77689ffaa Mon Sep 17 00:00:00 2001 From: A Date: Mon, 14 Nov 2022 10:50:41 +0100 Subject: [PATCH 05/55] fix comment --- integration/integration_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index 9d640b422..fa5d29a5c 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -587,7 +587,8 @@ func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing t.Logf("shuffling") err = sActor.Shuffle(formID) - // If the number of failing nodes is greater than the threshold, the shuffle will fail + // If the number of failing nodes is greater + // than the threshold, the shuffle will fail fmt.Println("threshold: ", numNodes/3) if failingNodes > numNodes/3 { require.Error(t, err) From 798c5dd1f118e60458ea8466657a36806f76384c Mon Sep 17 00:00:00 2001 From: A Date: Mon, 21 Nov 2022 10:05:38 +0100 Subject: [PATCH 06/55] load test and split files --- .github/workflows/go_integration_tests.yml | 2 +- integration/dvotingdela.go | 7 +- integration/integration_test.go | 813 +-------------------- integration/load_test.go | 421 +++++++++++ integration/performance_test.go | 6 +- integration/utility_functions.go | 524 +++++++++++++ integration/votes_test.go | 315 ++++++++ 7 files changed, 1270 insertions(+), 818 deletions(-) create mode 100644 integration/load_test.go create mode 100644 integration/utility_functions.go create mode 100644 integration/votes_test.go diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 791619e25..d0829b2b5 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -21,4 +21,4 @@ jobs: uses: actions/checkout@v2 - name: Run the integration test - run: go test -timeout 7m -run TestIntegration ./integration/... \ No newline at end of file + run: go test -timeout 15m -run TestIntegration ./integration/... \ No newline at end of file diff --git a/integration/dvotingdela.go b/integration/dvotingdela.go index cab73632e..d07a75493 100644 --- a/integration/dvotingdela.go +++ b/integration/dvotingdela.go @@ -109,7 +109,7 @@ type dVotingNode struct { // Creates n dela nodes using tempDir as root to file path and returns an array // of nodes or error -func setupDVotingNodes(t require.TestingT, numberOfNodes int, tempDir string, node dVotingCosiDela) []dVotingCosiDela { +func setupDVotingNodes(t require.TestingT, numberOfNodes int, tempDir string, nodeSetup dVotingCosiDela) []dVotingCosiDela { wait := sync.WaitGroup{} @@ -137,8 +137,9 @@ func setupDVotingNodes(t require.TestingT, numberOfNodes int, tempDir string, no delaNodes = append(delaNodes, node) dVotingNodes = append(dVotingNodes, node) } - if node != nil { - node.Setup(delaNodes...) + + if nodeSetup != nil { + nodeSetup.Setup(delaNodes...) } else { delaNodes[0].Setup(delaNodes[1:]...) } diff --git a/integration/integration_test.go b/integration/integration_test.go index fa5d29a5c..3274ee767 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1,45 +1,22 @@ package integration import ( - "bytes" - "context" - "crypto/sha256" - "encoding/base64" - "encoding/hex" "fmt" - "io" "math/rand" "os" "strconv" - "strings" - "sync" "testing" "time" - "github.com/dedis/d-voting/contracts/evoting" "github.com/dedis/d-voting/contracts/evoting/types" - "github.com/dedis/d-voting/internal/testing/fake" - "github.com/dedis/d-voting/services/dkg" _ "github.com/dedis/d-voting/services/dkg/pedersen/json" - "github.com/dedis/d-voting/services/shuffle" _ "github.com/dedis/d-voting/services/shuffle/neff/json" "github.com/rs/zerolog" "github.com/stretchr/testify/require" delaPkg "go.dedis.ch/dela" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "go.dedis.ch/kyber/v3" - "golang.org/x/xerrors" ) -const addAndWaitErr = "failed to addAndWait: %v" -var serdecontext = json.NewContext() // Check the shuffled votes versus the cast votes on a few nodes func TestIntegration(t *testing.T) { @@ -47,14 +24,6 @@ func TestIntegration(t *testing.T) { t.Run("10 nodes, 100 votes", getIntegrationTest(10, 100)) } -func TestBadVote(t *testing.T) { - t.Run("5 nodes, 10 votes including 5 bad votes", getIntegrationTestBadVote(5, 10, 5)) -} - -func TestRevote(t *testing.T) { - t.Run("5 nodes, 10 votes ", getIntegrationTestRevote(5, 10)) -} - func TestCrash(t *testing.T) { t.Run("5 nodes, 5 votes, 1 fail", getIntegrationTestCrash(5, 5, 1)) t.Run("5 nodes, 5 votes, 2 fails", getIntegrationTestCrash(5, 5, 2)) @@ -210,294 +179,6 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { } } -func getIntegrationTestBadVote(numNodes, numVotes, numBadVotes int) func(*testing.T) { - return func(t *testing.T) { - t.Parallel() - - adminID := "first admin" - - // ##### SETUP ENV ##### - // make tests reproducible - rand.Seed(1) - - delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) - - dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") - require.NoError(t, err) - - defer os.RemoveAll(dirPath) - - t.Logf("using temp dir %s", dirPath) - - // ##### CREATE NODES ##### - nodes := setupDVotingNodes(t, numNodes, dirPath, nil) - - signer := createDVotingAccess(t, nodes, dirPath) - - m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) - - err = grantAccess(m, signer) - require.NoError(t, err) - - for _, n := range nodes { - err = grantAccess(m, n.GetShuffleSigner()) - require.NoError(t, err) - } - - // ##### CREATE FORM ##### - formID, err := createForm(m, "Three votes form", adminID) - require.NoError(t, err) - - time.Sleep(time.Second * 1) - - // ##### SETUP DKG ##### - actor, err := initDkg(nodes, formID, m.m) - require.NoError(t, err) - - // ##### OPEN FORM ##### - err = openForm(m, formID) - require.NoError(t, err) - - formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) - - t.Logf("start casting votes") - form, err := getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - - //cast a vote with wrong answers: Should not be taken into account - - _, err = castVotesRandomly(m, actor, form, numVotes-numBadVotes) - require.NoError(t, err) - - err = castBadVote(m, actor, form, numBadVotes) - require.NoError(t, err) - - //castedVotes = append(castedVotes, badVotes...) - - //fmt.Println("casted votes:", castedVotes) - - // ##### CLOSE FORM ##### - err = closeForm(m, formID, adminID) - require.NoError(t, err) - - err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, - 5*time.Second) - require.NoError(t, err) - - // ##### SHUFFLE BALLOTS ##### - t.Logf("initializing shuffle") - sActor, err := initShuffle(nodes) - require.NoError(t, err) - - time.Sleep(time.Second * 1) - - t.Logf("shuffling") - err = sActor.Shuffle(formID) - require.NoError(t, err) - - err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, - numNodes, 2*time.Second*time.Duration(numNodes)) - require.NoError(t, err) - - // ##### SUBMIT PUBLIC SHARES ##### - t.Logf("submitting public shares") - - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - err = actor.ComputePubshares() - require.NoError(t, err) - - err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, - numNodes, 6*time.Second*time.Duration(numNodes)) - require.NoError(t, err) - - // ##### DECRYPT BALLOTS ##### - t.Logf("decrypting") - - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - t.Logf("PubsharesUnit: %v", form.PubsharesUnits) - require.NoError(t, err) - err = decryptBallots(m, actor, form) - require.NoError(t, err) - - err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, - numNodes, 1500*time.Millisecond*time.Duration(numVotes)) - require.NoError(t, err) - - t.Logf("get vote proof") - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - - fmt.Println("Title of the form : " + form.Configuration.MainTitle) - fmt.Println("ID of the form : " + string(form.FormID)) - fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) - fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) - - //require.Len(t, form.DecryptedBallots, numVotes-numBadVotes) - //should contains numBadVotes empty ballots - count := 0 - for _, ballot := range form.DecryptedBallots { - if ballotIsNull(ballot) { - count++ - } - //fmt.Println(fmt.Sprintf("%#v", ballot)) - } - fmt.Println(form.DecryptedBallots) - - require.Equal(t, numBadVotes, count) - - fmt.Println("closing nodes") - - err = closeNodes(nodes) - require.NoError(t, err) - - fmt.Println("test done") - } -} - -func getIntegrationTestRevote(numNodes, numVotes int) func(*testing.T) { - return func(t *testing.T) { - t.Parallel() - - adminID := "first admin" - - // ##### SETUP ENV ##### - // make tests reproducible - rand.Seed(1) - - delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) - - dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") - require.NoError(t, err) - - defer os.RemoveAll(dirPath) - - t.Logf("using temp dir %s", dirPath) - - // ##### CREATE NODES ##### - nodes := setupDVotingNodes(t, numNodes, dirPath, nil) - - signer := createDVotingAccess(t, nodes, dirPath) - - m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) - - err = grantAccess(m, signer) - require.NoError(t, err) - - for _, n := range nodes { - err = grantAccess(m, n.GetShuffleSigner()) - require.NoError(t, err) - } - - // ##### CREATE FORM ##### - formID, err := createForm(m, "Three votes form", adminID) - require.NoError(t, err) - - time.Sleep(time.Second * 1) - - // ##### SETUP DKG ##### - actor, err := initDkg(nodes, formID, m.m) - require.NoError(t, err) - - // ##### OPEN FORM ##### - err = openForm(m, formID) - require.NoError(t, err) - - formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) - - t.Logf("start casting votes") - form, err := getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - - _, err = castVotesRandomly(m, actor, form, numVotes) - require.NoError(t, err) - - castedVotes, err := castVotesRandomly(m, actor, form, numVotes) - require.NoError(t, err) - - fmt.Println("casted votes:", castedVotes) - - // ##### CLOSE FORM ##### - err = closeForm(m, formID, adminID) - require.NoError(t, err) - - err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, - 5*time.Second) - require.NoError(t, err) - - // ##### SHUFFLE BALLOTS ##### - t.Logf("initializing shuffle") - sActor, err := initShuffle(nodes) - require.NoError(t, err) - - time.Sleep(time.Second * 1) - - t.Logf("shuffling") - err = sActor.Shuffle(formID) - require.NoError(t, err) - - err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, - numNodes, 2*time.Second*time.Duration(numNodes)) - require.NoError(t, err) - - // ##### SUBMIT PUBLIC SHARES ##### - t.Logf("submitting public shares") - - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - err = actor.ComputePubshares() - require.NoError(t, err) - - err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, - numNodes, 6*time.Second*time.Duration(numNodes)) - require.NoError(t, err) - - // ##### DECRYPT BALLOTS ##### - t.Logf("decrypting") - - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - t.Logf("PubsharesUnit: %v", form.PubsharesUnits) - require.NoError(t, err) - err = decryptBallots(m, actor, form) - require.NoError(t, err) - - err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, - numNodes, 1500*time.Millisecond*time.Duration(numVotes)) - require.NoError(t, err) - - t.Logf("get vote proof") - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - - fmt.Println("Title of the form : " + form.Configuration.MainTitle) - fmt.Println("ID of the form : " + string(form.FormID)) - fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) - fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) - - require.Len(t, form.DecryptedBallots, len(castedVotes)) - - for _, b := range form.DecryptedBallots { - ok := false - for i, casted := range castedVotes { - if b.Equal(casted) { - ok = true - //remove the casted vote from the list - castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) - break - } - } - require.True(t, ok) - } - - fmt.Println("closing nodes") - - err = closeNodes(nodes) - require.NoError(t, err) - - fmt.Println("test done") - } -} - func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing.T) { return func(t *testing.T) { t.Parallel() @@ -587,7 +268,7 @@ func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing t.Logf("shuffling") err = sActor.Shuffle(formID) - // If the number of failing nodes is greater + // If the number of failing nodes is greater // than the threshold, the shuffle will fail fmt.Println("threshold: ", numNodes/3) if failingNodes > numNodes/3 { @@ -821,7 +502,7 @@ func getIntegrationBenchmark(numNodes, numVotes int) func(*testing.B) { defer os.RemoveAll(dirPath) // ##### CREATE NODES ##### - nodes := setupDVotingNodes(b, numNodes, dirPath,nil) + nodes := setupDVotingNodes(b, numNodes, dirPath, nil) signer := createDVotingAccess(b, nodes, dirPath) @@ -938,493 +619,3 @@ func getIntegrationBenchmark(numNodes, numVotes int) func(*testing.B) { // ----------------------------------------------------------------------------- // Utility functions - -func ballotIsNull(ballot types.Ballot) bool { - return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && - ballot.RankResultIDs == nil && ballot.RankResult == nil && - ballot.TextResultIDs == nil && ballot.TextResult == nil -} - -func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, - timeout time.Duration, retry int) txManager { - - client := client{ - srvc: firstNode.GetOrdering(), - mgr: firstNode.GetValidationSrv(), - } - - return txManager{ - m: signed.NewManager(signer, client), - n: firstNode, - t: timeout, - retry: retry, - } -} - -type txManager struct { - m txn.Manager - n dVotingCosiDela - t time.Duration - retry int -} - -func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { - for i := 0; i < m.retry; i++ { - sentTxn, err := m.m.Make(args...) - if err != nil { - return nil, xerrors.Errorf("failed to Make: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), m.t) - defer cancel() - - events := m.n.GetOrdering().Watch(ctx) - - err = m.n.GetPool().Add(sentTxn) - if err != nil { - return nil, xerrors.Errorf("failed to Add: %v", err) - } - - sentTxnID := sentTxn.GetID() - - accepted := isAccepted(events, sentTxnID) - if accepted { - return sentTxnID, nil - } - - err = m.m.Sync() - if err != nil { - return nil, xerrors.Errorf("failed to sync: %v", err) - } - - cancel() - } - - return nil, xerrors.Errorf("transaction not included after timeout: %v", args) -} - -// isAccepted returns true if the transaction was included then accepted -func isAccepted(events <-chan ordering.Event, txID []byte) bool { - for event := range events { - for _, result := range event.Transactions { - fetchedTxnID := result.GetTransaction().GetID() - - if bytes.Equal(txID, fetchedTxnID) { - accepted, _ := event.Transactions[0].GetStatus() - - return accepted - } - } - } - - return false -} - -func grantAccess(m txManager, signer crypto.Signer) error { - pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to GetPublicKey: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, - {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, - {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, - {Key: "access:grant_command", Value: []byte("all")}, - {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, - {Key: "access:command", Value: []byte("GRANT")}, - } - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf("failed to grantAccess: %v", err) - } - - return nil -} - -func createForm(m txManager, title string, admin string) ([]byte, error) { - // Define the configuration : - configuration := fake.BasicConfiguration - - createForm := types.CreateForm{ - Configuration: configuration, - AdminID: admin, - } - - data, err := createForm.Serialize(serdecontext) - if err != nil { - return nil, xerrors.Errorf("failed to serialize: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCreateForm)}, - } - - txID, err := m.addAndWait(args...) - if err != nil { - return nil, xerrors.Errorf(addAndWaitErr, err) - } - - // Calculate formID from - hash := sha256.New() - hash.Write(txID) - formID := hash.Sum(nil) - - return formID, nil -} - -func openForm(m txManager, formID []byte) error { - openForm := &types.OpenForm{ - FormID: hex.EncodeToString(formID), - } - - data, err := openForm.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize open form: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdOpenForm)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - return nil -} - -func getForm(formFac serde.Factory, formID []byte, - service ordering.Service) (types.Form, error) { - - form := types.Form{} - - proof, err := service.GetProof(formID) - if err != nil { - return form, xerrors.Errorf("failed to GetProof: %v", err) - } - - if proof == nil { - return form, xerrors.Errorf("form does not exist: %v", err) - } - - message, err := formFac.Deserialize(serdecontext, proof.GetValue()) - if err != nil { - return form, xerrors.Errorf("failed to deserialize Form: %v", err) - } - - form, ok := message.(types.Form) - if !ok { - return form, xerrors.Errorf("wrong message type: %T", message) - } - - return form, nil -} - -func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, - numberOfVotes int) ([]types.Ballot, error) { - - possibleBallots := []string{ - string("select:" + encodeID("bb") + ":0,0,1,0\n" + - "text:" + encodeID("ee") + ":eWVz\n\n"), //encoding of "yes" - string("select:" + encodeID("bb") + ":1,1,0,0\n" + - "text:" + encodeID("ee") + ":amE=\n\n"), //encoding of "ja - string("select:" + encodeID("bb") + ":0,0,0,1\n" + - "text:" + encodeID("ee") + ":b3Vp\n\n"), //encoding of "oui" - } - - votes := make([]types.Ballot, numberOfVotes) - - for i := 0; i < numberOfVotes; i++ { - randomIndex := rand.Intn(len(possibleBallots)) - vote := possibleBallots[randomIndex] - - ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) - if err != nil { - return nil, xerrors.Errorf("failed to marshallBallot: %v", err) - } - - userID := "user " + strconv.Itoa(i) - - castVote := types.CastVote{ - FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, - } - - data, err := castVote.Serialize(serdecontext) - if err != nil { - return nil, xerrors.Errorf("failed to serialize cast vote: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return nil, xerrors.Errorf(addAndWaitErr, err) - } - - var ballot types.Ballot - err = ballot.Unmarshal(vote, form) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal ballot: %v", err) - } - - votes[i] = ballot - } - - return votes, nil -} - -func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { - - possibleBallots := []string{ - string("select:" + encodeID("bb") + ":1,0,1,1\n" + - "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" - string("select:" + encodeID("bb") + ":1,1,1,1\n" + - "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" - - } - - for i := 0; i < numberOfBadVotes; i++ { - randomIndex := rand.Intn(len(possibleBallots)) - vote := possibleBallots[randomIndex] - - ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) - if err != nil { - return xerrors.Errorf("failed to marshallBallot: %v", err) - } - - userID := "badUser " + strconv.Itoa(i) - - castVote := types.CastVote{ - FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, - } - - data, err := castVote.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize cast vote: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - //votes[i] = ballot - } - - return nil -} - -func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { - - var ballot = make([]types.EGPair, chunks) - - buf := make([]byte, 29) - - for i := 0; i < chunks; i++ { - var K, C kyber.Point - var err error - - n, err := vote.Read(buf) - if err != nil { - return nil, xerrors.Errorf("failed to read: %v", err) - } - - K, C, _, err = actor.Encrypt(buf[:n]) - if err != nil { - return types.Ciphervote{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) - } - - ballot[i] = types.EGPair{ - K: K, - C: C, - } - } - - return ballot, nil -} - -func closeForm(m txManager, formID []byte, admin string) error { - closeForm := &types.CloseForm{ - FormID: hex.EncodeToString(formID), - UserID: admin, - } - - data, err := closeForm.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize open form: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCloseForm)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf("failed to Marshall closeForm: %v", err) - } - - return nil -} - -func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { - var actor dkg.Actor - var err error - - for _, node := range nodes { - d := node.(dVotingNode).GetDkg() - - // put Listen in a goroutine to optimize for speed - actor, err = d.Listen(formID, m) - if err != nil { - return nil, xerrors.Errorf("failed to GetDkg: %v", err) - } - } - - _, err = actor.Setup() - if err != nil { - return nil, xerrors.Errorf("failed to Setup: %v", err) - } - - return actor, nil -} - -func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { - var sActor shuffle.Actor - - for _, node := range nodes { - client := client{ - srvc: node.GetOrdering(), - mgr: node.GetValidationSrv(), - } - - var err error - shuffler := node.GetShuffle() - - sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) - if err != nil { - return nil, xerrors.Errorf("failed to init Shuffle: %v", err) - } - } - - return sActor, nil -} - -func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { - if form.Status != types.PubSharesSubmitted { - return xerrors.Errorf("cannot decrypt: not all pubShares submitted") - } - - decryptBallots := types.CombineShares{ - FormID: form.FormID, - } - - data, err := decryptBallots.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize ballots: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCombineShares)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - return nil -} - -func closeNodes(nodes []dVotingCosiDela) error { - wait := sync.WaitGroup{} - wait.Add(len(nodes)) - - for _, n := range nodes { - go func(node dVotingNode) { - defer wait.Done() - node.GetOrdering().Close() - }(n.(dVotingNode)) - } - - done := make(chan struct{}) - - go func() { - wait.Wait() - close(done) - }() - - select { - case <-done: - return nil - case <-time.After(time.Second * 30): - return xerrors.New("failed to close: timeout") - } -} - -func encodeID(ID string) types.ID { - return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) -} - -// waitForStatus polls the nodes until they all updated to the expected status -// for the given form. An error is raised if the timeout expires. -func waitForStatus(status types.Status, formFac types.FormFactory, - formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { - - expiration := time.Now().Add(timeOut) - - isOK := func() (bool, error) { - for _, node := range nodes { - form, err := getForm(formFac, formID, node.GetOrdering()) - if err != nil { - return false, xerrors.Errorf("failed to get form: %v", err) - } - - if form.Status != status { - return false, nil - } - } - - return true, nil - } - - for { - if time.Now().After(expiration) { - return xerrors.New("status check expired") - } - - ok, err := isOK() - if err != nil { - return xerrors.Errorf("failed to check status: %v", err) - } - - if ok { - return nil - } - - time.Sleep(time.Millisecond * 100) - } -} diff --git a/integration/load_test.go b/integration/load_test.go new file mode 100644 index 000000000..3eed0de2f --- /dev/null +++ b/integration/load_test.go @@ -0,0 +1,421 @@ +package integration + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/http" + "os" + "reflect" + "strconv" + "sync" + "testing" + "time" + + "github.com/dedis/d-voting/contracts/evoting/types" + "github.com/dedis/d-voting/internal/testing/fake" + ptypes "github.com/dedis/d-voting/proxy/types" + "github.com/stretchr/testify/require" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/util/encoding" +) + +// Check the shuffled votes versus the cast votes on a few nodes +func TestLoad(t *testing.T) { + var err error + numNodes := 10 + + n, ok := os.LookupEnv("NNODES") + if ok { + numNodes, err = strconv.Atoi(n) + require.NoError(t, err) + } + t.Run("Basic configuration", getLoadTest(numNodes, 10, 60, 1)) +} + +func getLoadTest(numNodes, numVotesPerSec, numSec, numForm int) func(*testing.T) { + return func(t *testing.T) { + + proxyList := make([]string, numNodes) + + for i := 0; i < numNodes; i++ { + proxyList[i] = fmt.Sprintf("http://localhost:%d", 9080+i) + t.Log(proxyList[i]) + } + + var wg sync.WaitGroup + + for i := 0; i < numForm; i++ { + t.Log("Starting worker", i) + wg.Add(1) + + go startFormProcessLoad(&wg, numNodes, numVotesPerSec, numSec, proxyList, t, numForm) + time.Sleep(2 * time.Second) + + } + + t.Log("Waiting for workers to finish") + wg.Wait() + + } +} + +func startFormProcessLoad(wg *sync.WaitGroup, numNodes, numVotesPerSec, numSec int, proxyArray []string, t *testing.T, numForm int) { + defer wg.Done() + rand.Seed(0) + + const contentType = "application/json" + secretkeyBuf, err := hex.DecodeString("28912721dfd507e198b31602fb67824856eb5a674c021d49fdccbe52f0234409") + require.NoError(t, err) + + secret := suite.Scalar() + err = secret.UnmarshalBinary(secretkeyBuf) + require.NoError(t, err) + + // ###################################### CREATE SIMPLE FORM ###### + + t.Log("Create form") + + configuration := fake.BasicConfiguration + + createSimpleFormRequest := ptypes.CreateFormRequest{ + Configuration: configuration, + AdminID: "adminId", + } + + signed, err := createSignedRequest(secret, createSimpleFormRequest) + require.NoError(t, err) + + resp, err := http.Post(proxyArray[0]+"/evoting/forms", contentType, bytes.NewBuffer(signed)) + require.NoError(t, err) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s %s", resp.Status,body) + + + + t.Log("response body:", string(body)) + resp.Body.Close() + + var createFormResponse ptypes.CreateFormResponse + + err = json.Unmarshal(body, &createFormResponse) + require.NoError(t, err) + + formID := createFormResponse.FormID + + t.Logf("ID of the form : " + formID) + + // ##################################### SETUP DKG ######################### + + t.Log("Init DKG") + + for i := 0; i < numNodes; i++ { + t.Log("Node" + strconv.Itoa(i+1)) + t.Log(proxyArray[i]) + err = initDKG(secret, proxyArray[i], formID, t) + require.NoError(t, err) + + } + t.Log("Setup DKG") + + msg := ptypes.UpdateDKG{ + Action: "setup", + } + signed, err = createSignedRequest(secret, msg) + require.NoError(t, err) + + req, err := http.NewRequest(http.MethodPut, proxyArray[0]+"/evoting/services/dkg/actors/"+formID, bytes.NewBuffer(signed)) + require.NoError(t, err) + + resp, err = http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) + + // ##################################### OPEN FORM ##################### + // Wait for DKG setup + timeTable := make([]float64, 5) + oldTime := time.Now() + + err = waitForDKG(proxyArray[0], formID, time.Second*100, t) + require.NoError(t, err) + + currentTime := time.Now() + diff := currentTime.Sub(oldTime) + timeTable[0] = diff.Seconds() + t.Logf("DKG setup takes: %v sec", diff.Seconds()) + + randomproxy := "http://localhost:9081" + t.Logf("Open form send to proxy %v", randomproxy) + + _, err = updateForm(secret, randomproxy, formID, "open", t) + require.NoError(t, err) + // ##################################### GET FORM INFO ################# + + proxyAddr1 := proxyArray[0] + time.Sleep(time.Second * 5) + + getFormResponse := getFormInfo(proxyAddr1, formID, t) + formpubkey := getFormResponse.Pubkey + formStatus := getFormResponse.Status + BallotSize := getFormResponse.BallotSize + Chunksperballot := chunksPerBallot(BallotSize) + + t.Logf("Publickey of the form : " + formpubkey) + t.Logf("Status of the form : %v", formStatus) + + require.NoError(t, err) + t.Logf("BallotSize of the form : %v", BallotSize) + t.Logf("Chunksperballot of the form : %v", Chunksperballot) + + // Get form public key + pubKey, err := encoding.StringHexToPoint(suite, formpubkey) + require.NoError(t, err) + + // ##################################### CAST BALLOTS ###################### + + votesfrontend := castVotesLoad(numVotesPerSec, numSec, BallotSize, Chunksperballot, formID, contentType, proxyArray, pubKey, secret, t) + + // ############################# CLOSE FORM FOR REAL ################### + randomproxy = proxyArray[rand.Intn(len(proxyArray))] + + t.Logf("Close form (for real) send to proxy %v", randomproxy) + + _, err = updateForm(secret, randomproxy, formID, "close", t) + require.NoError(t, err) + + time.Sleep(time.Second * 3) + + getFormResponse = getFormInfo(proxyAddr1, formID, t) + formStatus = getFormResponse.Status + + t.Logf("Status of the form : %v", formStatus) + require.Equal(t, uint16(2), formStatus) + + // ###################################### SHUFFLE BALLOTS ################## + time.Sleep(time.Second * 5) + + t.Log("shuffle ballots") + + shuffleBallotsRequest := ptypes.UpdateShuffle{ + Action: "shuffle", + } + + signed, err = createSignedRequest(secret, shuffleBallotsRequest) + require.NoError(t, err) + + randomproxy = proxyArray[rand.Intn(len(proxyArray))] + + req, err = http.NewRequest(http.MethodPut, randomproxy+"/evoting/services/shuffle/"+formID, bytes.NewBuffer(signed)) + require.NoError(t, err) + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) + + resp, err = http.DefaultClient.Do(req) + require.NoError(t, err) + + currentTime = time.Now() + diff = currentTime.Sub(oldTime) + timeTable[1] = diff.Seconds() + t.Logf("Shuffle takes: %v sec", diff.Seconds()) + + body, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + t.Log("Response body: " + string(body)) + resp.Body.Close() + + getFormResponse = getFormInfo(proxyAddr1, formID, t) + formStatus = getFormResponse.Status + + err = waitForFormStatus(proxyAddr1, formID, uint16(3), time.Second*100, t) + require.NoError(t, err) + + t.Logf("Status of the form : %v", formStatus) + require.Equal(t, uint16(3), formStatus) + + // ###################################### REQUEST PUBLIC SHARES ############ + time.Sleep(time.Second * 5) + + t.Log("request public shares") + + randomproxy = proxyArray[rand.Intn(len(proxyArray))] + oldTime = time.Now() + + _, err = updateDKG(secret, randomproxy, formID, "computePubshares", t) + require.NoError(t, err) + + currentTime = time.Now() + diff = currentTime.Sub(oldTime) + timeTable[2] = diff.Seconds() + + t.Logf("Request public share takes: %v sec", diff.Seconds()) + + time.Sleep(10 * time.Second) + + getFormResponse = getFormInfo(proxyAddr1, formID, t) + formStatus = getFormResponse.Status + + oldTime = time.Now() + err = waitForFormStatus(proxyAddr1, formID, uint16(4), time.Second*300, t) + require.NoError(t, err) + + currentTime = time.Now() + diff = currentTime.Sub(oldTime) + timeTable[4] = diff.Seconds() + t.Logf("Status goes to 4 takes: %v sec", diff.Seconds()) + + t.Logf("Status of the form : %v", formStatus) + require.Equal(t, uint16(4), formStatus) + + // ###################################### DECRYPT BALLOTS ################## + time.Sleep(time.Second * 5) + + t.Log("decrypt ballots") + + randomproxy = proxyArray[rand.Intn(len(proxyArray))] + oldTime = time.Now() + + _, err = updateForm(secret, randomproxy, formID, "combineShares", t) + require.NoError(t, err) + + currentTime = time.Now() + diff = currentTime.Sub(oldTime) + timeTable[3] = diff.Seconds() + + t.Logf("decryption takes: %v sec", diff.Seconds()) + + time.Sleep(time.Second * 7) + + getFormResponse = getFormInfo(proxyAddr1, formID, t) + formStatus = getFormResponse.Status + + err = waitForFormStatus(proxyAddr1, formID, uint16(5), time.Second*100, t) + require.NoError(t, err) + + t.Logf("Status of the form : %v", formStatus) + require.Equal(t, uint16(5), formStatus) + + //#################################### VALIDATE FORM RESULT ############## + + tmpBallots := getFormResponse.Result + var tmpCount bool + require.Equal(t, len(tmpBallots), numVotesPerSec*numSec) + + for _, ballotIntem := range tmpBallots { + tmpComp := ballotIntem + tmpCount = false + for _, voteFront := range votesfrontend { + // t.Logf("voteFront: %v", voteFront) + // t.Logf("tmpComp: %v", tmpComp) + + tmpCount = reflect.DeepEqual(tmpComp, voteFront) + // t.Logf("tmpCount: %v", tmpCount) + + if tmpCount { + break + } + } + } + + require.True(t, tmpCount, "front end votes are different from decrypted votes") + t.Logf("DKG setup time : %v", timeTable[0]) + t.Logf("shuffle time : %v", timeTable[1]) + t.Logf("Public share time : %v", timeTable[2]) + t.Logf("Status goes to 4 takes: %v sec", diff.Seconds()) + t.Logf("decryption time : %v", timeTable[3]) +} + +func castVotesLoad(numVotesPerSec, numSec, BallotSize, chunksPerBallot int, formID, contentType string, proxyArray []string, pubKey kyber.Point, secret kyber.Scalar, t *testing.T) []types.Ballot { + t.Log("cast ballots") + + //make List of ballots + b1 := string("select:" + encodeIDBallot("bb") + ":0,0,1,0\n" + "text:" + encodeIDBallot("ee") + ":eWVz\n\n") //encoding of "yes" + + numVotes := numVotesPerSec * numSec + + ballotList := make([]string, numVotes) + for i := 1; i <= numVotes; i++ { + ballotList[i-1] = b1 + } + + votesfrontend := make([]types.Ballot, numVotes) + + fakeConfiguration := fake.BasicConfiguration + + for i := 0; i < numVotes; i++ { + + var bMarshal types.Ballot + form := types.Form{ + Configuration: fakeConfiguration, + FormID: formID, + BallotSize: BallotSize, + } + + err := bMarshal.Unmarshal(ballotList[i], form) + require.NoError(t, err) + + votesfrontend[i] = bMarshal + } + proxyCount := len(proxyArray) + + // all ballots are identical + ballot, err := marshallBallotManual(b1, pubKey, chunksPerBallot) + require.NoError(t, err) + + for i := 0; i < numSec; i++ { + // send the votes asynchrounously and wait for the response + + for j := 0; j < numVotesPerSec; j++ { + randomproxy := proxyArray[rand.Intn(proxyCount)] + castVoteRequest := ptypes.CastVoteRequest{ + UserID: "user"+strconv.Itoa(i*numVotesPerSec+j), + Ballot: ballot, + } + go cast(false,castVoteRequest, contentType, randomproxy, formID, secret, t) + + } + t.Logf("casted votes %d", (i+1)*numVotesPerSec) + time.Sleep(time.Second) + + + } + + time.Sleep(time.Second * 30) + + return votesfrontend +} + + +func cast(isRetry bool, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, t *testing.T) { + + + + t.Logf("cast ballot to proxy %v", randomproxy) + + // t.Logf("vote is: %v", castVoteRequest) + signed, err := createSignedRequest(secret, castVoteRequest) + require.NoError(t, err) + + resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) + require.NoError(t, err) + + if http.StatusOK != resp.StatusCode && !isRetry { + t.Logf("unexpected status: %s retry", resp.Status) + cast(true,castVoteRequest, contentType, randomproxy, formID, secret, t) + return + } + + responseBody,err:=ioutil.ReadAll(resp.Body) + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s %s", resp.Status,responseBody) + + _, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + resp.Body.Close() +} diff --git a/integration/performance_test.go b/integration/performance_test.go index 5b986a5c2..fe698a8d8 100644 --- a/integration/performance_test.go +++ b/integration/performance_test.go @@ -46,7 +46,7 @@ func BenchmarkIntegration_CustomVotesScenario(b *testing.B) { b.Logf("using temp dir %s", dirPath) // ##### CREATE NODES ##### - nodes := setupDVotingNodes(b, numNodes, dirPath,nil) + nodes := setupDVotingNodes(b, numNodes, dirPath, nil) signer := createDVotingAccess(b, nodes, dirPath) @@ -233,8 +233,8 @@ func castVotesNChunks(m txManager, actor dkg.Actor, form types.Form, castVote := types.CastVote{ FormID: form.FormID, - UserID: userID, - Ballot: ballot, + UserID: userID, + Ballot: ballot, } data, err := castVote.Serialize(serdecontext) diff --git a/integration/utility_functions.go b/integration/utility_functions.go new file mode 100644 index 000000000..43bf862d8 --- /dev/null +++ b/integration/utility_functions.go @@ -0,0 +1,524 @@ +package integration + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "io" + "math/rand" + "strconv" + "strings" + "sync" + "time" + + "github.com/dedis/d-voting/contracts/evoting" + "github.com/dedis/d-voting/contracts/evoting/types" + "github.com/dedis/d-voting/internal/testing/fake" + "github.com/dedis/d-voting/services/dkg" + "github.com/dedis/d-voting/services/shuffle" + "go.dedis.ch/dela/core/execution/native" + "go.dedis.ch/dela/core/ordering" + "go.dedis.ch/dela/core/txn" + "go.dedis.ch/dela/core/txn/signed" + "go.dedis.ch/dela/crypto" + "go.dedis.ch/dela/serde" + "go.dedis.ch/dela/serde/json" + "go.dedis.ch/kyber/v3" + "golang.org/x/xerrors" +) + +const addAndWaitErr = "failed to addAndWait: %v" + +var serdecontext = json.NewContext() + +func ballotIsNull(ballot types.Ballot) bool { + return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && + ballot.RankResultIDs == nil && ballot.RankResult == nil && + ballot.TextResultIDs == nil && ballot.TextResult == nil +} + +func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, + timeout time.Duration, retry int) txManager { + + client := client{ + srvc: firstNode.GetOrdering(), + mgr: firstNode.GetValidationSrv(), + } + + return txManager{ + m: signed.NewManager(signer, client), + n: firstNode, + t: timeout, + retry: retry, + } +} + +type txManager struct { + m txn.Manager + n dVotingCosiDela + t time.Duration + retry int +} + +func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { + for i := 0; i < m.retry; i++ { + sentTxn, err := m.m.Make(args...) + if err != nil { + return nil, xerrors.Errorf("failed to Make: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), m.t) + defer cancel() + + events := m.n.GetOrdering().Watch(ctx) + + err = m.n.GetPool().Add(sentTxn) + if err != nil { + return nil, xerrors.Errorf("failed to Add: %v", err) + } + + sentTxnID := sentTxn.GetID() + + accepted := isAccepted(events, sentTxnID) + if accepted { + return sentTxnID, nil + } + + err = m.m.Sync() + if err != nil { + return nil, xerrors.Errorf("failed to sync: %v", err) + } + + cancel() + } + + return nil, xerrors.Errorf("transaction not included after timeout: %v", args) +} + +// isAccepted returns true if the transaction was included then accepted +func isAccepted(events <-chan ordering.Event, txID []byte) bool { + for event := range events { + for _, result := range event.Transactions { + fetchedTxnID := result.GetTransaction().GetID() + + if bytes.Equal(txID, fetchedTxnID) { + accepted, _ := event.Transactions[0].GetStatus() + + return accepted + } + } + } + + return false +} + +func grantAccess(m txManager, signer crypto.Signer) error { + pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() + if err != nil { + return xerrors.Errorf("failed to GetPublicKey: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, + {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, + {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, + {Key: "access:grant_command", Value: []byte("all")}, + {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, + {Key: "access:command", Value: []byte("GRANT")}, + } + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf("failed to grantAccess: %v", err) + } + + return nil +} + +func createForm(m txManager, title string, admin string) ([]byte, error) { + // Define the configuration : + configuration := fake.BasicConfiguration + + createForm := types.CreateForm{ + Configuration: configuration, + AdminID: admin, + } + + data, err := createForm.Serialize(serdecontext) + if err != nil { + return nil, xerrors.Errorf("failed to serialize: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCreateForm)}, + } + + txID, err := m.addAndWait(args...) + if err != nil { + return nil, xerrors.Errorf(addAndWaitErr, err) + } + + // Calculate formID from + hash := sha256.New() + hash.Write(txID) + formID := hash.Sum(nil) + + return formID, nil +} + +func openForm(m txManager, formID []byte) error { + openForm := &types.OpenForm{ + FormID: hex.EncodeToString(formID), + } + + data, err := openForm.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize open form: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdOpenForm)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + return nil +} + +func getForm(formFac serde.Factory, formID []byte, + service ordering.Service) (types.Form, error) { + + form := types.Form{} + + proof, err := service.GetProof(formID) + if err != nil { + return form, xerrors.Errorf("failed to GetProof: %v", err) + } + + if proof == nil { + return form, xerrors.Errorf("form does not exist: %v", err) + } + + message, err := formFac.Deserialize(serdecontext, proof.GetValue()) + if err != nil { + return form, xerrors.Errorf("failed to deserialize Form: %v", err) + } + + form, ok := message.(types.Form) + if !ok { + return form, xerrors.Errorf("wrong message type: %T", message) + } + + return form, nil +} + +func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, + numberOfVotes int) ([]types.Ballot, error) { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":0,0,1,0\n" + + "text:" + encodeID("ee") + ":eWVz\n\n"), //encoding of "yes" + string("select:" + encodeID("bb") + ":1,1,0,0\n" + + "text:" + encodeID("ee") + ":amE=\n\n"), //encoding of "ja + string("select:" + encodeID("bb") + ":0,0,0,1\n" + + "text:" + encodeID("ee") + ":b3Vp\n\n"), //encoding of "oui" + } + + votes := make([]types.Ballot, numberOfVotes) + + for i := 0; i < numberOfVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return nil, xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "user " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return nil, xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return nil, xerrors.Errorf(addAndWaitErr, err) + } + + var ballot types.Ballot + err = ballot.Unmarshal(vote, form) + if err != nil { + return nil, xerrors.Errorf("failed to unmarshal ballot: %v", err) + } + + votes[i] = ballot + } + + return votes, nil +} + +func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":1,0,1,1\n" + + "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" + string("select:" + encodeID("bb") + ":1,1,1,1\n" + + "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" + + } + + for i := 0; i < numberOfBadVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "badUser " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + //votes[i] = ballot + } + + return nil +} + +func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { + + var ballot = make([]types.EGPair, chunks) + + buf := make([]byte, 29) + + for i := 0; i < chunks; i++ { + var K, C kyber.Point + var err error + + n, err := vote.Read(buf) + if err != nil { + return nil, xerrors.Errorf("failed to read: %v", err) + } + + K, C, _, err = actor.Encrypt(buf[:n]) + if err != nil { + return types.Ciphervote{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) + } + + ballot[i] = types.EGPair{ + K: K, + C: C, + } + } + + return ballot, nil +} + +func closeForm(m txManager, formID []byte, admin string) error { + closeForm := &types.CloseForm{ + FormID: hex.EncodeToString(formID), + UserID: admin, + } + + data, err := closeForm.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize open form: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCloseForm)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf("failed to Marshall closeForm: %v", err) + } + + return nil +} + +func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { + var actor dkg.Actor + var err error + + for _, node := range nodes { + d := node.(dVotingNode).GetDkg() + + // put Listen in a goroutine to optimize for speed + actor, err = d.Listen(formID, m) + if err != nil { + return nil, xerrors.Errorf("failed to GetDkg: %v", err) + } + } + + _, err = actor.Setup() + if err != nil { + return nil, xerrors.Errorf("failed to Setup: %v", err) + } + + return actor, nil +} + +func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { + var sActor shuffle.Actor + + for _, node := range nodes { + client := client{ + srvc: node.GetOrdering(), + mgr: node.GetValidationSrv(), + } + + var err error + shuffler := node.GetShuffle() + + sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) + if err != nil { + return nil, xerrors.Errorf("failed to init Shuffle: %v", err) + } + } + + return sActor, nil +} + +func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { + if form.Status != types.PubSharesSubmitted { + return xerrors.Errorf("cannot decrypt: not all pubShares submitted") + } + + decryptBallots := types.CombineShares{ + FormID: form.FormID, + } + + data, err := decryptBallots.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize ballots: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCombineShares)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + return nil +} + +func closeNodes(nodes []dVotingCosiDela) error { + wait := sync.WaitGroup{} + wait.Add(len(nodes)) + + for _, n := range nodes { + go func(node dVotingNode) { + defer wait.Done() + node.GetOrdering().Close() + }(n.(dVotingNode)) + } + + done := make(chan struct{}) + + go func() { + wait.Wait() + close(done) + }() + + select { + case <-done: + return nil + case <-time.After(time.Second * 30): + return xerrors.New("failed to close: timeout") + } +} + +func encodeID(ID string) types.ID { + return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) +} + +// waitForStatus polls the nodes until they all updated to the expected status +// for the given form. An error is raised if the timeout expires. +func waitForStatus(status types.Status, formFac types.FormFactory, + formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { + + expiration := time.Now().Add(timeOut) + + isOK := func() (bool, error) { + for _, node := range nodes { + form, err := getForm(formFac, formID, node.GetOrdering()) + if err != nil { + return false, xerrors.Errorf("failed to get form: %v", err) + } + + if form.Status != status { + return false, nil + } + } + + return true, nil + } + + for { + if time.Now().After(expiration) { + return xerrors.New("status check expired") + } + + ok, err := isOK() + if err != nil { + return xerrors.Errorf("failed to check status: %v", err) + } + + if ok { + return nil + } + + time.Sleep(time.Millisecond * 100) + } +} diff --git a/integration/votes_test.go b/integration/votes_test.go new file mode 100644 index 000000000..4280eb8f3 --- /dev/null +++ b/integration/votes_test.go @@ -0,0 +1,315 @@ +package integration + +import ( + "fmt" + "math/rand" + "os" + "strconv" + + "testing" + "time" + + "github.com/dedis/d-voting/contracts/evoting/types" + _ "github.com/dedis/d-voting/services/dkg/pedersen/json" + _ "github.com/dedis/d-voting/services/shuffle/neff/json" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + delaPkg "go.dedis.ch/dela" +) + +func TestBadVote(t *testing.T) { + t.Run("5 nodes, 10 votes including 5 bad votes", getIntegrationTestBadVote(5, 10, 5)) +} + +func TestRevote(t *testing.T) { + t.Run("5 nodes, 10 votes ", getIntegrationTestRevote(5, 10, 10)) +} + +func getIntegrationTestBadVote(numNodes, numVotes, numBadVotes int) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(t, err) + + defer os.RemoveAll(dirPath) + + t.Logf("using temp dir %s", dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) + + signer := createDVotingAccess(t, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(t, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(t, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(t, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(t, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + t.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + //cast a vote with wrong answers: Should not be taken into account + + _, err = castVotesRandomly(m, actor, form, numVotes-numBadVotes) + require.NoError(t, err) + + err = castBadVote(m, actor, form, numBadVotes) + require.NoError(t, err) + + //castedVotes = append(castedVotes, badVotes...) + + //fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(t, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(t, err) + + // ##### SHUFFLE BALLOTS ##### + t.Logf("initializing shuffle") + sActor, err := initShuffle(nodes) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + t.Logf("shuffling") + err = sActor.Shuffle(formID) + require.NoError(t, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### SUBMIT PUBLIC SHARES ##### + t.Logf("submitting public shares") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + err = actor.ComputePubshares() + require.NoError(t, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### DECRYPT BALLOTS ##### + t.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + t.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(t, err) + err = decryptBallots(m, actor, form) + require.NoError(t, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(t, err) + + t.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + //require.Len(t, form.DecryptedBallots, numVotes-numBadVotes) + //should contains numBadVotes empty ballots + count := 0 + for _, ballot := range form.DecryptedBallots { + if ballotIsNull(ballot) { + count++ + } + //fmt.Println(fmt.Sprintf("%#v", ballot)) + } + fmt.Println(form.DecryptedBallots) + + require.Equal(t, numBadVotes, count) + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(t, err) + + fmt.Println("test done") + } +} + +func getIntegrationTestRevote(numNodes, numVotes, numRevotes int) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + require.LessOrEqual(t, numRevotes, numVotes) + + adminID := "first admin" + + // ##### SETUP ENV ##### + // make tests reproducible + rand.Seed(1) + + delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) + + dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") + require.NoError(t, err) + + defer os.RemoveAll(dirPath) + + t.Logf("using temp dir %s", dirPath) + + // ##### CREATE NODES ##### + nodes := setupDVotingNodes(t, numNodes, dirPath, nil) + + signer := createDVotingAccess(t, nodes, dirPath) + + m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) + + err = grantAccess(m, signer) + require.NoError(t, err) + + for _, n := range nodes { + err = grantAccess(m, n.GetShuffleSigner()) + require.NoError(t, err) + } + + // ##### CREATE FORM ##### + formID, err := createForm(m, "Three votes form", adminID) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + // ##### SETUP DKG ##### + actor, err := initDkg(nodes, formID, m.m) + require.NoError(t, err) + + // ##### OPEN FORM ##### + err = openForm(m, formID) + require.NoError(t, err) + + formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) + + t.Logf("start casting votes") + form, err := getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + _, err = castVotesRandomly(m, actor, form, numVotes) + require.NoError(t, err) + + castedVotes, err := castVotesRandomly(m, actor, form, numRevotes) + require.NoError(t, err) + + fmt.Println("casted votes:", castedVotes) + + // ##### CLOSE FORM ##### + err = closeForm(m, formID, adminID) + require.NoError(t, err) + + err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, + 5*time.Second) + require.NoError(t, err) + + // ##### SHUFFLE BALLOTS ##### + t.Logf("initializing shuffle") + sActor, err := initShuffle(nodes) + require.NoError(t, err) + + time.Sleep(time.Second * 1) + + t.Logf("shuffling") + err = sActor.Shuffle(formID) + require.NoError(t, err) + + err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, + numNodes, 2*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### SUBMIT PUBLIC SHARES ##### + t.Logf("submitting public shares") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + err = actor.ComputePubshares() + require.NoError(t, err) + + err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, + numNodes, 6*time.Second*time.Duration(numNodes)) + require.NoError(t, err) + + // ##### DECRYPT BALLOTS ##### + t.Logf("decrypting") + + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + t.Logf("PubsharesUnit: %v", form.PubsharesUnits) + require.NoError(t, err) + err = decryptBallots(m, actor, form) + require.NoError(t, err) + + err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, + numNodes, 1500*time.Millisecond*time.Duration(numVotes)) + require.NoError(t, err) + + t.Logf("get vote proof") + form, err = getForm(formFac, formID, nodes[0].GetOrdering()) + require.NoError(t, err) + + fmt.Println("Title of the form : " + form.Configuration.MainTitle) + fmt.Println("ID of the form : " + string(form.FormID)) + fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) + fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) + + require.Len(t, form.DecryptedBallots, len(castedVotes)) + + for _, b := range form.DecryptedBallots { + ok := false + for i, casted := range castedVotes { + if b.Equal(casted) { + ok = true + //remove the casted vote from the list + castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) + break + } + } + require.True(t, ok) + } + + fmt.Println("closing nodes") + + err = closeNodes(nodes) + require.NoError(t, err) + + fmt.Println("test done") + } +} From 16cf2d592ebe35c1ec4549f4cc3f5cc034299996 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Mon, 21 Nov 2022 15:25:36 +0100 Subject: [PATCH 07/55] Checking if Txn is included added to proxy. --- proxy/election.go | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index f04ed11cf..f55bb5431 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -642,24 +642,47 @@ func (h *form) submitAndWaitForTxn(ctx context.Context, cmd evoting.Command, return nil, xerrors.Errorf("failed to create transaction: %v", err) } - watchCtx, cancel := context.WithTimeout(ctx, inclusionTimeout) - defer cancel() + //watchCtx, cancel := context.WithTimeout(ctx, inclusionTimeout) //plus besoin de timeout + //defer cancel() - events := h.orderingSvc.Watch(watchCtx) + // events := h.orderingSvc.Watch(watchCtx) il faudra implementer ca lorsque l'on devra appeler checkTxnIncluded - err = h.pool.Add(tx) + err = h.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, + // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID + // le frontend peut alors lui meme verifier si la transaction est bien incluse dans le bloc + // en passant par le proxy et sa fonction checkTxnIncluded if err != nil { return nil, xerrors.Errorf("failed to add transaction to the pool: %v", err) } - + /* err = h.waitForTxnID(events, tx.GetID()) if err != nil { return nil, xerrors.Errorf("failed to wait for transaction: %v", err) } - +*/ return tx.GetID(), nil } +//A function that checks if a transaction is included in a block +func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, error) { + for event := range events { + for _, res := range event.Transactions { + if !bytes.Equal(res.GetTransaction().GetID(), ID) { + continue + } + + ok, msg := res.GetStatus() + if !ok { + return false,xerrors.Errorf("transaction %x denied : %s", ID, msg) + } + + return true,nil + } + } + + return false, nil +} + // createTransaction creates a transaction with the given command and payload. func createTransaction(manager txn.Manager, commandType evoting.Command, commandArg string, buf []byte) (txn.Transaction, error) { From 8d742cba51056888067ff8e1dfa68cf2c2bf9df4 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Mon, 21 Nov 2022 15:48:35 +0100 Subject: [PATCH 08/55] renaming of a variable for clarity --- proxy/election.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index f55bb5431..eeb66f3f9 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -626,18 +626,18 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, // submitAndWaitForTxn submits a transaction and waits for it to be included. // Returns the transaction ID. -func (h *form) submitAndWaitForTxn(ctx context.Context, cmd evoting.Command, +func (formProxy *form) submitAndWaitForTxn(ctx context.Context, cmd evoting.Command, cmdArg string, payload []byte) ([]byte, error) { - h.Lock() - defer h.Unlock() + formProxy.Lock() + defer formProxy.Unlock() - err := h.mngr.Sync() + err := formProxy.mngr.Sync() if err != nil { return nil, xerrors.Errorf("failed to sync manager: %v", err) } - tx, err := createTransaction(h.mngr, cmd, cmdArg, payload) + tx, err := createTransaction(formProxy.mngr, cmd, cmdArg, payload) if err != nil { return nil, xerrors.Errorf("failed to create transaction: %v", err) } @@ -647,7 +647,7 @@ func (h *form) submitAndWaitForTxn(ctx context.Context, cmd evoting.Command, // events := h.orderingSvc.Watch(watchCtx) il faudra implementer ca lorsque l'on devra appeler checkTxnIncluded - err = h.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, + err = formProxy.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID // le frontend peut alors lui meme verifier si la transaction est bien incluse dans le bloc // en passant par le proxy et sa fonction checkTxnIncluded From a511d42d249d12e5ff9016166a96b1f8aa2a1622 Mon Sep 17 00:00:00 2001 From: A Date: Tue, 22 Nov 2022 08:52:19 +0100 Subject: [PATCH 09/55] edit transaction system --- contracts/evoting/controller/action.go | 4 +- proxy/election.go | 108 ++++++++++++++++--------- proxy/types/election.go | 7 ++ 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/contracts/evoting/controller/action.go b/contracts/evoting/controller/action.go index 6c36acf2e..c0e186bbf 100644 --- a/contracts/evoting/controller/action.go +++ b/contracts/evoting/controller/action.go @@ -91,7 +91,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { return xerrors.Errorf("failed to resolve ordering.Service: %v", err) } - var blocks *blockstore.InDisk + var blocks blockstore.BlockStore err = ctx.Injector.Resolve(&blocks) if err != nil { return xerrors.Errorf("failed to resolve blockstore.InDisk: %v", err) @@ -163,7 +163,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { return xerrors.Errorf("failed to unmarshal proxy key: %v", err) } - ep := eproxy.NewForm(ordering, mngr, p, sjson.NewContext(), formFac, proxykey) + ep := eproxy.NewForm(ordering, mngr, p, sjson.NewContext(), formFac, proxykey,blocks) router := mux.NewRouter() diff --git a/proxy/election.go b/proxy/election.go index eeb66f3f9..9473a91b2 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -18,6 +18,8 @@ import ( "go.dedis.ch/dela" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" + "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" + btypes "go.dedis.ch/dela/core/ordering/cosipbft/types" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/pool" "go.dedis.ch/dela/serde" @@ -36,7 +38,7 @@ func getSignedErr(err error) error { // NewForm returns a new initialized form proxy func NewForm(srv ordering.Service, mngr txn.Manager, p pool.Pool, - ctx serde.Context, fac serde.Factory, pk kyber.Point) Form { + ctx serde.Context, fac serde.Factory, pk kyber.Point, blocks blockstore.BlockStore) Form { logger := dela.Logger.With().Timestamp().Str("role", "evoting-proxy").Logger() @@ -44,10 +46,11 @@ func NewForm(srv ordering.Service, mngr txn.Manager, p pool.Pool, logger: logger, orderingSvc: srv, context: ctx, - formFac: fac, + formFac: fac, mngr: mngr, pool: p, pk: pk, + blocks: blocks, } } @@ -60,10 +63,11 @@ type form struct { orderingSvc ordering.Service logger zerolog.Logger context serde.Context - formFac serde.Factory + formFac serde.Factory mngr txn.Manager pool pool.Pool pk kyber.Point + blocks blockstore.BlockStore } // NewForm implements proxy.Proxy @@ -98,7 +102,7 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txID, err := h.submitAndWaitForTxn(r.Context(), evoting.CmdCreateForm, evoting.FormArg, data) + txID, _, err := h.submitTxn(r.Context(), evoting.CmdCreateForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return @@ -166,7 +170,7 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { ciphervote := make(types.Ciphervote, len(req.Ballot)) - // encrypt the vote + // encrypt the vote for i, egpair := range req.Ballot { k := suite.Point() @@ -192,8 +196,8 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { castVote := types.CastVote{ FormID: formID, - UserID: req.UserID, - Ballot: ciphervote, + UserID: req.UserID, + Ballot: ciphervote, } // serialize the vote @@ -205,11 +209,27 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - _, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdCastVote, evoting.FormArg, data) + transactionID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCastVote, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + + // send the transactionID and the LastBlock link to the client + response := ptypes.CastVoteResponse{ + TransactionID: transactionID, + LastBlock: lastBlock, + } + + // sign the response + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, "failed to write in ResponseWriter: "+err.Error(), + http.StatusInternalServerError) + return + } + } // EditForm implements proxy.Proxy @@ -282,11 +302,13 @@ func (h *form) openForm(elecID string, w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - _, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdOpenForm, evoting.FormArg, data) + txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdOpenForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + + } // closeForm closes a form. @@ -305,11 +327,12 @@ func (h *form) closeForm(formIDHex string, w http.ResponseWriter, r *http.Reques } // create the transaction and add it to the pool - _, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdCloseForm, evoting.FormArg, data) + _, _, err = h.submitTxn(r.Context(), evoting.CmdCloseForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + } // combineShares decrypts the shuffled ballots in a form. @@ -340,7 +363,7 @@ func (h *form) combineShares(formIDHex string, w http.ResponseWriter, r *http.Re } // create the transaction and add it to the pool - _, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + _, _, err = h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return @@ -363,7 +386,7 @@ func (h *form) cancelForm(formIDHex string, w http.ResponseWriter, r *http.Reque } // create the transaction and add it to the pool - _, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdCancelForm, evoting.FormArg, data) + _, _, err = h.submitTxn(r.Context(), evoting.CmdCancelForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return @@ -413,7 +436,7 @@ func (h *form) Form(w http.ResponseWriter, r *http.Request) { } response := ptypes.GetFormResponse{ - FormID: string(form.FormID), + FormID: string(form.FormID), Configuration: form.Configuration, Status: uint16(form.Status), Pubkey: hex.EncodeToString(pubkeyBuf), @@ -468,9 +491,9 @@ func (h *form) Forms(w http.ResponseWriter, r *http.Request) { info := ptypes.LightForm{ FormID: string(form.FormID), - Title: form.Configuration.MainTitle, - Status: uint16(form.Status), - Pubkey: hex.EncodeToString(pubkeyBuf), + Title: form.Configuration.MainTitle, + Status: uint16(form.Status), + Pubkey: hex.EncodeToString(pubkeyBuf), } allFormsInfo[i] = info @@ -539,7 +562,7 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - _, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdDeleteForm, evoting.FormArg, data) + _, _, err = h.submitTxn(r.Context(), evoting.CmdDeleteForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return @@ -624,22 +647,22 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, return form, nil } -// submitAndWaitForTxn submits a transaction and waits for it to be included. +// submitTxn submits a transaction and waits for it to be included. // Returns the transaction ID. -func (formProxy *form) submitAndWaitForTxn(ctx context.Context, cmd evoting.Command, - cmdArg string, payload []byte) ([]byte, error) { +func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, + cmdArg string, payload []byte) ([]byte, btypes.BlockLink, error) { - formProxy.Lock() - defer formProxy.Unlock() + h.Lock() + defer h.Unlock() - err := formProxy.mngr.Sync() + err := h.mngr.Sync() if err != nil { - return nil, xerrors.Errorf("failed to sync manager: %v", err) + return nil, nil, xerrors.Errorf("failed to sync manager: %v", err) } - tx, err := createTransaction(formProxy.mngr, cmd, cmdArg, payload) + tx, err := createTransaction(h.mngr, cmd, cmdArg, payload) if err != nil { - return nil, xerrors.Errorf("failed to create transaction: %v", err) + return nil, nil, xerrors.Errorf("failed to create transaction: %v", err) } //watchCtx, cancel := context.WithTimeout(ctx, inclusionTimeout) //plus besoin de timeout @@ -647,23 +670,30 @@ func (formProxy *form) submitAndWaitForTxn(ctx context.Context, cmd evoting.Comm // events := h.orderingSvc.Watch(watchCtx) il faudra implementer ca lorsque l'on devra appeler checkTxnIncluded - err = formProxy.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, - // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID - // le frontend peut alors lui meme verifier si la transaction est bien incluse dans le bloc - // en passant par le proxy et sa fonction checkTxnIncluded + lastBlock, err := h.blocks.Last() if err != nil { - return nil, xerrors.Errorf("failed to add transaction to the pool: %v", err) + return nil, nil, xerrors.Errorf("failed to get last block: %v", err) } - /* - err = h.waitForTxnID(events, tx.GetID()) + + err = h.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, + // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID + // le frontend peut alors lui meme verifier si la transaction est bien incluse dans le bloc + // en passant par le proxy et sa fonction checkTxnIncluded + if err != nil { - return nil, xerrors.Errorf("failed to wait for transaction: %v", err) + return nil, nil, xerrors.Errorf("failed to add transaction to the pool: %v", err) } -*/ - return tx.GetID(), nil + /* + err = h.waitForTxnID(events, tx.GetID()) + if err != nil { + return nil, xerrors.Errorf("failed to wait for transaction: %v", err) + } + */ + + return tx.GetID(), lastBlock, nil } -//A function that checks if a transaction is included in a block +// A function that checks if a transaction is included in a block func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, error) { for event := range events { for _, res := range event.Transactions { @@ -673,10 +703,10 @@ func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, ok, msg := res.GetStatus() if !ok { - return false,xerrors.Errorf("transaction %x denied : %s", ID, msg) + return false, xerrors.Errorf("transaction %x denied : %s", ID, msg) } - return true,nil + return true, nil } } diff --git a/proxy/types/election.go b/proxy/types/election.go index 17832d51f..b21905b32 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -2,6 +2,7 @@ package types import ( etypes "github.com/dedis/d-voting/contracts/evoting/types" + btypes "go.dedis.ch/dela/core/ordering/cosipbft/types" ) // CreateFormRequest defines the HTTP request for creating a form @@ -22,6 +23,12 @@ type CastVoteRequest struct { Ballot CiphervoteJSON } +// CastVoteResponse defines the HTTP response when casting a vote +type CastVoteResponse struct { + TransactionID []byte + LastBlock btypes.BlockLink // last block of the chain when the transaction was added to the pool +} + // CiphervoteJSON is the JSON representation of a ciphervote type CiphervoteJSON []EGPairJSON From b568b36551f50cb05a5db5926a91d3c3fe4591ab Mon Sep 17 00:00:00 2001 From: A Date: Tue, 22 Nov 2022 09:19:19 +0100 Subject: [PATCH 10/55] add transaction response --- proxy/election.go | 65 +++++++++++++++++++++++++++-------------- proxy/types/election.go | 11 +++---- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index 9473a91b2..142a9d4f9 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -108,6 +108,8 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { return } + // TODO: wait for the transaction to be included in a block?? + // hash the transaction hash := sha256.New() hash.Write(txID) @@ -209,26 +211,14 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - transactionID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCastVote, evoting.FormArg, data) + txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } - // send the transactionID and the LastBlock link to the client - response := ptypes.CastVoteResponse{ - TransactionID: transactionID, - LastBlock: lastBlock, - } - - // sign the response - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, "failed to write in ResponseWriter: "+err.Error(), - http.StatusInternalServerError) - return - } + //send the transaction + sendTransactionResponse(w, txID, lastBlock) } @@ -288,9 +278,9 @@ func (h *form) EditForm(w http.ResponseWriter, r *http.Request) { // openForm allows opening a form, which sets the public key based on // the DKG actor. -func (h *form) openForm(elecID string, w http.ResponseWriter, r *http.Request) { +func (h *form) openForm(formID string, w http.ResponseWriter, r *http.Request) { openForm := types.OpenForm{ - FormID: elecID, + FormID: formID, } // serialize the transaction @@ -308,7 +298,8 @@ func (h *form) openForm(elecID string, w http.ResponseWriter, r *http.Request) { return } - + //send the transaction + sendTransactionResponse(w, txID, lastBlock) } // closeForm closes a form. @@ -327,12 +318,15 @@ func (h *form) closeForm(formIDHex string, w http.ResponseWriter, r *http.Reques } // create the transaction and add it to the pool - _, _, err = h.submitTxn(r.Context(), evoting.CmdCloseForm, evoting.FormArg, data) + txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + //send the transaction + sendTransactionResponse(w, txID, lastBlock) + } // combineShares decrypts the shuffled ballots in a form. @@ -363,11 +357,14 @@ func (h *form) combineShares(formIDHex string, w http.ResponseWriter, r *http.Re } // create the transaction and add it to the pool - _, _, err = h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + + //send the transaction + sendTransactionResponse(w, txID, lastBlock) } // cancelForm cancels a form. @@ -386,11 +383,14 @@ func (h *form) cancelForm(formIDHex string, w http.ResponseWriter, r *http.Reque } // create the transaction and add it to the pool - _, _, err = h.submitTxn(r.Context(), evoting.CmdCancelForm, evoting.FormArg, data) + txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + + //send the transaction + sendTransactionResponse(w, txID, lastBlock) } // Form implements proxy.Proxy. The request should not be signed because it @@ -562,11 +562,14 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - _, _, err = h.submitTxn(r.Context(), evoting.CmdDeleteForm, evoting.FormArg, data) + txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } + + //send the transaction + sendTransactionResponse(w, txID, lastBlock) } // waitForTxnID blocks until `ID` is included or `events` is closed. @@ -713,6 +716,24 @@ func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, return false, nil } +func sendTransactionResponse(w http.ResponseWriter, txnID []byte, lastBlock btypes.BlockLink) { + response := ptypes.TransactionResponse{ + TransactionID: txnID, + LastBlock: lastBlock, + } + + // sign the response + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, "failed to write in ResponseWriter: "+err.Error(), + http.StatusInternalServerError) + return + } + +} + + // createTransaction creates a transaction with the given command and payload. func createTransaction(manager txn.Manager, commandType evoting.Command, commandArg string, buf []byte) (txn.Transaction, error) { diff --git a/proxy/types/election.go b/proxy/types/election.go index b21905b32..fa0dedcbc 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -23,11 +23,6 @@ type CastVoteRequest struct { Ballot CiphervoteJSON } -// CastVoteResponse defines the HTTP response when casting a vote -type CastVoteResponse struct { - TransactionID []byte - LastBlock btypes.BlockLink // last block of the chain when the transaction was added to the pool -} // CiphervoteJSON is the JSON representation of a ciphervote type CiphervoteJSON []EGPairJSON @@ -38,6 +33,12 @@ type EGPairJSON struct { C []byte } +// TransactionResponse defines the HTTP response when sending a transaction to the blockchain +type TransactionResponse struct { + TransactionID []byte + LastBlock btypes.BlockLink // last block of the chain when the transaction was added to the pool +} + // UpdateFormRequest defines the HTTP request for updating a form type UpdateFormRequest struct { Action string From 184ee54b523d2c79c01b0935963e1fa82017f0d9 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 22 Nov 2022 09:26:23 +0100 Subject: [PATCH 11/55] Added function in APi for txn --- proxy/mod.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proxy/mod.go b/proxy/mod.go index 78bb9a8ec..91afeaad5 100644 --- a/proxy/mod.go +++ b/proxy/mod.go @@ -34,6 +34,8 @@ type Form interface { Form(http.ResponseWriter, *http.Request) // DELETE /forms/{formID} DeleteForm(http.ResponseWriter, *http.Request) + // GET /forms/transactions/{txID} + IsTxnIncluded(http.ResponseWriter, *http.Request) } // DKG defines the public HTTP API of the DKG service From 7ee1055a1a7c033e38df1c9499068fa57a32bbf2 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 22 Nov 2022 09:28:32 +0100 Subject: [PATCH 12/55] Endpoint added --- contracts/evoting/controller/action.go | 1 + proxy/mod.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/evoting/controller/action.go b/contracts/evoting/controller/action.go index c0e186bbf..704bfc4b3 100644 --- a/contracts/evoting/controller/action.go +++ b/contracts/evoting/controller/action.go @@ -175,6 +175,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { router.HandleFunc(formIDPath, eproxy.AllowCORS).Methods("OPTIONS") router.HandleFunc(formIDPath, ep.DeleteForm).Methods("DELETE") router.HandleFunc(formIDPath+"/vote", ep.NewFormVote).Methods("POST") + router.HandleFunc(formPath+"/transactions/{txnID}", ep.IsTxnIncluded).Methods("GET") router.NotFoundHandler = http.HandlerFunc(eproxy.NotFoundHandler) router.MethodNotAllowedHandler = http.HandlerFunc(eproxy.NotAllowedHandler) diff --git a/proxy/mod.go b/proxy/mod.go index 91afeaad5..f480aa505 100644 --- a/proxy/mod.go +++ b/proxy/mod.go @@ -34,7 +34,7 @@ type Form interface { Form(http.ResponseWriter, *http.Request) // DELETE /forms/{formID} DeleteForm(http.ResponseWriter, *http.Request) - // GET /forms/transactions/{txID} + // GET /forms/transactions/{txnID} IsTxnIncluded(http.ResponseWriter, *http.Request) } From ee93ae8a5f2b3450868382b654ddf72120975bb8 Mon Sep 17 00:00:00 2001 From: A Date: Tue, 22 Nov 2022 09:28:53 +0100 Subject: [PATCH 13/55] renaming --- proxy/election.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index 142a9d4f9..068b018f8 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -102,7 +102,7 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txID, _, err := h.submitTxn(r.Context(), evoting.CmdCreateForm, evoting.FormArg, data) + txnID, _, err := h.submitTxn(r.Context(), evoting.CmdCreateForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return @@ -112,7 +112,7 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { // hash the transaction hash := sha256.New() - hash.Write(txID) + hash.Write(txnID) formID := hash.Sum(nil) // return the formID @@ -211,14 +211,14 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - sendTransactionResponse(w, txID, lastBlock) + sendTransactionResponse(w, txnID, lastBlock) } @@ -292,14 +292,14 @@ func (h *form) openForm(formID string, w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdOpenForm, evoting.FormArg, data) + txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdOpenForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - sendTransactionResponse(w, txID, lastBlock) + sendTransactionResponse(w, txnID, lastBlock) } // closeForm closes a form. @@ -318,14 +318,14 @@ func (h *form) closeForm(formIDHex string, w http.ResponseWriter, r *http.Reques } // create the transaction and add it to the pool - txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - sendTransactionResponse(w, txID, lastBlock) + sendTransactionResponse(w, txnID, lastBlock) } @@ -357,14 +357,14 @@ func (h *form) combineShares(formIDHex string, w http.ResponseWriter, r *http.Re } // create the transaction and add it to the pool - txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - sendTransactionResponse(w, txID, lastBlock) + sendTransactionResponse(w, txnID, lastBlock) } // cancelForm cancels a form. @@ -383,14 +383,14 @@ func (h *form) cancelForm(formIDHex string, w http.ResponseWriter, r *http.Reque } // create the transaction and add it to the pool - txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - sendTransactionResponse(w, txID, lastBlock) + sendTransactionResponse(w, txnID, lastBlock) } // Form implements proxy.Proxy. The request should not be signed because it @@ -562,14 +562,14 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - sendTransactionResponse(w, txID, lastBlock) + sendTransactionResponse(w, txnID, lastBlock) } // waitForTxnID blocks until `ID` is included or `events` is closed. From 3c995ce7d1fef43c241567d19482ca7ab4d4b7f1 Mon Sep 17 00:00:00 2001 From: A Date: Tue, 22 Nov 2022 09:35:16 +0100 Subject: [PATCH 14/55] change TransactionResonse to TransactionInfo --- proxy/election.go | 16 ++++++++-------- proxy/types/election.go | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index 068b018f8..b6961de44 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -218,7 +218,7 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { } //send the transaction - sendTransactionResponse(w, txnID, lastBlock) + sendTransactionInfo(w, txnID, lastBlock) } @@ -299,7 +299,7 @@ func (h *form) openForm(formID string, w http.ResponseWriter, r *http.Request) { } //send the transaction - sendTransactionResponse(w, txnID, lastBlock) + sendTransactionInfo(w, txnID, lastBlock) } // closeForm closes a form. @@ -325,7 +325,7 @@ func (h *form) closeForm(formIDHex string, w http.ResponseWriter, r *http.Reques } //send the transaction - sendTransactionResponse(w, txnID, lastBlock) + sendTransactionInfo(w, txnID, lastBlock) } @@ -364,7 +364,7 @@ func (h *form) combineShares(formIDHex string, w http.ResponseWriter, r *http.Re } //send the transaction - sendTransactionResponse(w, txnID, lastBlock) + sendTransactionInfo(w, txnID, lastBlock) } // cancelForm cancels a form. @@ -390,7 +390,7 @@ func (h *form) cancelForm(formIDHex string, w http.ResponseWriter, r *http.Reque } //send the transaction - sendTransactionResponse(w, txnID, lastBlock) + sendTransactionInfo(w, txnID, lastBlock) } // Form implements proxy.Proxy. The request should not be signed because it @@ -569,7 +569,7 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { } //send the transaction - sendTransactionResponse(w, txnID, lastBlock) + sendTransactionInfo(w, txnID, lastBlock) } // waitForTxnID blocks until `ID` is included or `events` is closed. @@ -716,8 +716,8 @@ func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, return false, nil } -func sendTransactionResponse(w http.ResponseWriter, txnID []byte, lastBlock btypes.BlockLink) { - response := ptypes.TransactionResponse{ +func sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlock btypes.BlockLink) { + response := ptypes.TransactionInfo{ TransactionID: txnID, LastBlock: lastBlock, } diff --git a/proxy/types/election.go b/proxy/types/election.go index fa0dedcbc..5d518e544 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -33,8 +33,8 @@ type EGPairJSON struct { C []byte } -// TransactionResponse defines the HTTP response when sending a transaction to the blockchain -type TransactionResponse struct { +// TransactionInfo defines the HTTP response when sending a transaction to the blockchain +type TransactionInfo struct { TransactionID []byte LastBlock btypes.BlockLink // last block of the chain when the transaction was added to the pool } From c078bb87eced292437d9da96f7c44832eea1e86c Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 22 Nov 2022 09:35:39 +0100 Subject: [PATCH 15/55] isTxnIncluded added --- proxy/election.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proxy/election.go b/proxy/election.go index 068b018f8..6ba638858 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -572,6 +572,12 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { sendTransactionResponse(w, txnID, lastBlock) } +//IsTxnIncluded +func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { + + +} + // waitForTxnID blocks until `ID` is included or `events` is closed. func (h *form) waitForTxnID(events <-chan ordering.Event, ID []byte) error { for event := range events { From ecb5830f6df8d48c3f45c5fe1aedd0b47ec6c4d1 Mon Sep 17 00:00:00 2001 From: A Date: Tue, 22 Nov 2022 09:53:19 +0100 Subject: [PATCH 16/55] change blocklink to it's index --- proxy/election.go | 47 ++++++++++++++++++++++++++++++++++------- proxy/types/election.go | 3 +-- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index a21dd1815..02995a794 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "sync" "github.com/dedis/d-voting/contracts/evoting" @@ -574,7 +575,36 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { //IsTxnIncluded func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { + // fetch the transactionID and block link + vars := mux.Vars(r) + + // check if the transactionID and block link are present + if vars == nil || vars["transactionID"] == "" || vars["blockLink"] == "" { + http.Error(w, fmt.Sprintf("transactionID or blockLink not found: %v", vars), http.StatusInternalServerError) + return + } + + // get the transactionID as a []byte and the block link as a uint64 + transactionID, err := hex.DecodeString(vars["transactionID"]) + if err != nil { + http.Error(w, "failed to decode transactionID: "+err.Error(), http.StatusInternalServerError) + return + } + + blockLink, err := strconv.ParseUint(vars["blockLink"], 10, 64) + if err != nil { + http.Error(w, "failed to parse blockLink: "+err.Error(), http.StatusInternalServerError) + return + } + fmt.Println("transactionID",transactionID) + fmt.Println("blockLink",blockLink) + + + + + + } @@ -659,19 +689,19 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, // submitTxn submits a transaction and waits for it to be included. // Returns the transaction ID. func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, - cmdArg string, payload []byte) ([]byte, btypes.BlockLink, error) { + cmdArg string, payload []byte) ([]byte, uint64, error) { h.Lock() defer h.Unlock() err := h.mngr.Sync() if err != nil { - return nil, nil, xerrors.Errorf("failed to sync manager: %v", err) + return nil, 0, xerrors.Errorf("failed to sync manager: %v", err) } tx, err := createTransaction(h.mngr, cmd, cmdArg, payload) if err != nil { - return nil, nil, xerrors.Errorf("failed to create transaction: %v", err) + return nil, 0, xerrors.Errorf("failed to create transaction: %v", err) } //watchCtx, cancel := context.WithTimeout(ctx, inclusionTimeout) //plus besoin de timeout @@ -681,8 +711,9 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, lastBlock, err := h.blocks.Last() if err != nil { - return nil, nil, xerrors.Errorf("failed to get last block: %v", err) + return nil, 0, xerrors.Errorf("failed to get last block: %v", err) } + lastBlockIdx:=lastBlock.GetBlock().GetIndex() err = h.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID @@ -690,7 +721,7 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, // en passant par le proxy et sa fonction checkTxnIncluded if err != nil { - return nil, nil, xerrors.Errorf("failed to add transaction to the pool: %v", err) + return nil, 0, xerrors.Errorf("failed to add transaction to the pool: %v", err) } /* err = h.waitForTxnID(events, tx.GetID()) @@ -699,7 +730,7 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, } */ - return tx.GetID(), lastBlock, nil + return tx.GetID(), lastBlockIdx, nil } // A function that checks if a transaction is included in a block @@ -722,10 +753,10 @@ func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, return false, nil } -func sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlock btypes.BlockLink) { +func sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64) { response := ptypes.TransactionInfo{ TransactionID: txnID, - LastBlock: lastBlock, + LastBlockIdx: lastBlockIdx, } // sign the response diff --git a/proxy/types/election.go b/proxy/types/election.go index 5d518e544..2974990cb 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -2,7 +2,6 @@ package types import ( etypes "github.com/dedis/d-voting/contracts/evoting/types" - btypes "go.dedis.ch/dela/core/ordering/cosipbft/types" ) // CreateFormRequest defines the HTTP request for creating a form @@ -36,7 +35,7 @@ type EGPairJSON struct { // TransactionInfo defines the HTTP response when sending a transaction to the blockchain type TransactionInfo struct { TransactionID []byte - LastBlock btypes.BlockLink // last block of the chain when the transaction was added to the pool + LastBlockIdx uint64 // last block of the chain when the transaction was added to the pool } // UpdateFormRequest defines the HTTP request for updating a form From 2d9584f5d8c9d0422203830d2c28a867e1bb6b97 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 28 Nov 2022 11:00:13 +0100 Subject: [PATCH 17/55] advanced the proxy implementation of the new transaction mechanism --- contracts/evoting/controller/action.go | 3 +- proxy/election.go | 217 ++++++++++++++++++++++--- proxy/types/election.go | 22 +++ 3 files changed, 217 insertions(+), 25 deletions(-) diff --git a/contracts/evoting/controller/action.go b/contracts/evoting/controller/action.go index 704bfc4b3..9bf33cd3e 100644 --- a/contracts/evoting/controller/action.go +++ b/contracts/evoting/controller/action.go @@ -97,6 +97,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { return xerrors.Errorf("failed to resolve blockstore.InDisk: %v", err) } + var ordering ordering.Service err = ctx.Injector.Resolve(&ordering) @@ -163,7 +164,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { return xerrors.Errorf("failed to unmarshal proxy key: %v", err) } - ep := eproxy.NewForm(ordering, mngr, p, sjson.NewContext(), formFac, proxykey,blocks) + ep := eproxy.NewForm(ordering, mngr, p, sjson.NewContext(), formFac, proxykey,blocks,signer) router := mux.NewRouter() diff --git a/proxy/election.go b/proxy/election.go index 02995a794..99fc4990e 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -10,6 +10,7 @@ import ( "net/http" "strconv" "sync" + "time" "github.com/dedis/d-voting/contracts/evoting" "github.com/dedis/d-voting/contracts/evoting/types" @@ -20,7 +21,7 @@ import ( "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - btypes "go.dedis.ch/dela/core/ordering/cosipbft/types" + "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/pool" "go.dedis.ch/dela/serde" @@ -29,6 +30,10 @@ import ( "golang.org/x/xerrors" ) +const ( + maxTimeTransactionCheck = 10 * time.Minute +) + func newSignedErr(err error) error { return xerrors.Errorf("failed to created signed request: %v", err) } @@ -39,7 +44,7 @@ func getSignedErr(err error) error { // NewForm returns a new initialized form proxy func NewForm(srv ordering.Service, mngr txn.Manager, p pool.Pool, - ctx serde.Context, fac serde.Factory, pk kyber.Point, blocks blockstore.BlockStore) Form { + ctx serde.Context, fac serde.Factory, pk kyber.Point, blocks blockstore.BlockStore, signer crypto.Signer ) Form { logger := dela.Logger.With().Timestamp().Str("role", "evoting-proxy").Logger() @@ -52,6 +57,7 @@ func NewForm(srv ordering.Service, mngr txn.Manager, p pool.Pool, pool: p, pk: pk, blocks: blocks, + signer: signer, } } @@ -69,6 +75,7 @@ type form struct { pool pool.Pool pk kyber.Point blocks blockstore.BlockStore + signer crypto.Signer } // NewForm implements proxy.Proxy @@ -219,7 +226,7 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { } //send the transaction - sendTransactionInfo(w, txnID, lastBlock) + h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) } @@ -300,7 +307,7 @@ func (h *form) openForm(formID string, w http.ResponseWriter, r *http.Request) { } //send the transaction - sendTransactionInfo(w, txnID, lastBlock) + h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) } // closeForm closes a form. @@ -326,7 +333,7 @@ func (h *form) closeForm(formIDHex string, w http.ResponseWriter, r *http.Reques } //send the transaction - sendTransactionInfo(w, txnID, lastBlock) + h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) } @@ -365,7 +372,7 @@ func (h *form) combineShares(formIDHex string, w http.ResponseWriter, r *http.Re } //send the transaction - sendTransactionInfo(w, txnID, lastBlock) + h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) } // cancelForm cancels a form. @@ -391,7 +398,7 @@ func (h *form) cancelForm(formIDHex string, w http.ResponseWriter, r *http.Reque } //send the transaction - sendTransactionInfo(w, txnID, lastBlock) + h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) } // Form implements proxy.Proxy. The request should not be signed because it @@ -570,7 +577,7 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { } //send the transaction - sendTransactionInfo(w, txnID, lastBlock) + h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) } //IsTxnIncluded @@ -578,36 +585,165 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { // fetch the transactionID and block link vars := mux.Vars(r) - // check if the transactionID and block link are present - if vars == nil || vars["transactionID"] == "" || vars["blockLink"] == "" { - http.Error(w, fmt.Sprintf("transactionID or blockLink not found: %v", vars), http.StatusInternalServerError) + // check if the all the parameters from the TransactionInfo struct are present + if vars == nil || vars["Status"] == "" || vars["transactionID"] == "" || vars["LastBlockIdx"] == "" || vars["Time"] == "" || vars["Hash"] == "" || vars["Signature"] == "" { + http.Error(w, fmt.Sprintf("transactionID, LastBlockIdx, Time or Hash not found: %v", vars ), http.StatusInternalServerError) + return + } + + // get the status of the transaction as byte + var status ptypes.TransactionStatus + switch vars["Status"] { + case "0": + status = ptypes.UnknownTransactionStatus + case "1": // Accepted + http.Error(w, fmt.Sprintf("transaction already included: %v", vars ), http.StatusInternalServerError) + case "2": // Rejected + http.Error(w, fmt.Sprintf("transaction won't be included: %v", vars ), http.StatusInternalServerError) + + default: + http.Error(w, fmt.Sprintf("transaction status not found: %v", vars["Status"]), http.StatusInternalServerError) return } - // get the transactionID as a []byte and the block link as a uint64 + + + // get the transactionID as []byte transactionID, err := hex.DecodeString(vars["transactionID"]) if err != nil { - http.Error(w, "failed to decode transactionID: "+err.Error(), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("failed to decode transactionID: %v", err), http.StatusInternalServerError) return } - blockLink, err := strconv.ParseUint(vars["blockLink"], 10, 64) + // get the LastBlockIdx as uint64 + lastBlockIdx, err := strconv.ParseUint(vars["LastBlockIdx"], 10, 64) + if err != nil { - http.Error(w, "failed to parse blockLink: "+err.Error(), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("failed to parse LastBlockIdx: %v", err), http.StatusInternalServerError) return } - - fmt.Println("transactionID",transactionID) - fmt.Println("blockLink",blockLink) + // get the Time as int64 + transactionTime, err := strconv.ParseInt(vars["Time"], 10, 64) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse Time: %v", err), http.StatusInternalServerError) + return + } + + // get the Hash as []byte + hash, err := hex.DecodeString(vars["Hash"]) + if err != nil { + http.Error(w, fmt.Sprintf("failed to decode Hash: %v", err), http.StatusInternalServerError) + return + } + + // get the Signature as []byte + signatureBin, err := hex.DecodeString(vars["Signature"]) + if err != nil { + http.Error(w, fmt.Sprintf("failed to decode Signature: %v", err), http.StatusInternalServerError) + return + } + + signature,err := h.signer.GetSignatureFactory().SignatureOf(h.context, signatureBin) + if err != nil { + http.Error(w, fmt.Sprintf("failed to get Signature: %v", err), http.StatusInternalServerError) + return + } + // check if the hash is valid + if !h.checkHash(status, transactionID, lastBlockIdx, transactionTime, hash) { + http.Error(w, "invalid hash", http.StatusInternalServerError) + return + } + + // check if the signature is valid + if !h.checkSignature(hash, signature) { + http.Error(w, "invalid signature", http.StatusInternalServerError) + return + } + + // check if if was submited not to long ago + if time.Now().Unix() - transactionTime > int64(maxTimeTransactionCheck) { + http.Error(w, "the transaction is too old", http.StatusInternalServerError) + return + } + + if time.Now().Unix() - transactionTime < 0 { + http.Error(w, "the transaction is from the future", http.StatusInternalServerError) + return + } + + // check if the transaction is included in the blockchain + newStatus, idx := h.checkTxnIncluded(transactionID, lastBlockIdx) + + + err = h.sendTransactionInfo(w, transactionID, idx,newStatus) + if err != nil { + http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) + return + } +} + +// checkHash checks if the hash is valid +func (h *form) checkHash(status ptypes.TransactionStatus , transactionID []byte, LastBlockIdx uint64, Time int64, Hash []byte) bool { + // create the hash + hash := sha256.New() + hash.Write([]byte{byte(status)}) + hash.Write(transactionID) + hash.Write([]byte(strconv.FormatUint(LastBlockIdx, 10))) + hash.Write([]byte(strconv.FormatInt(Time, 10))) + + // check if the hash is valid + if !bytes.Equal(hash.Sum(nil), Hash) { + return false + } + + return true +} + +// checkSignature checks if the signature is valid +func (h *form) checkSignature(Hash []byte, Signature crypto.Signature ) bool { + // check if the signature is valid + + return h.signer.GetPublicKey().Verify(Hash, Signature) == nil +} + +// checkTxnIncluded checks if the transaction is included in the blockchain +func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptypes.TransactionStatus,uint64) { + // first get the block + idx := lastBlockIdx + + for (true) { + + + blockLink, err := h.blocks.GetByIndex(idx) + // if we reached the end of the blockchain + if err != nil { + return ptypes.UnknownTransactionStatus,idx-1 + } + + transactions := blockLink.GetBlock().GetTransactions() + for _, txn := range transactions { + if bytes.Equal(txn.GetID(), transactionID) { + return ptypes.IncludedTransaction ,blockLink.GetBlock().GetIndex() + } + + } + + idx++ +} + + return ptypes.RejectedTransaction,idx-1 + + } + // waitForTxnID blocks until `ID` is included or `events` is closed. func (h *form) waitForTxnID(events <-chan ordering.Event, ID []byte) error { for event := range events { @@ -734,7 +870,7 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, } // A function that checks if a transaction is included in a block -func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, error) { +/*func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, error) { for event := range events { for _, res := range event.Transactions { if !bytes.Equal(res.GetTransaction().GetID(), ID) { @@ -751,23 +887,56 @@ func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, } return false, nil -} +}*/ + +func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (error) { + + time:=time.Now().Unix() + hash:=sha256.New() + + // write status which is a byte to the hash as a []byte + hash.Write([]byte{byte(status)}) + hash.Write(txnID) + hash.Write([]byte(strconv.FormatUint(lastBlockIdx,10))) + hash.Write([]byte(strconv.FormatInt(time,10))) + + finalHash:=hash.Sum(nil) + + signature,err:=h.signer.Sign(finalHash) + + if err!=nil{ + return xerrors.Errorf("failed to sign transaction info: %v", err) + } + //convert signature to []byte + signatureBin,err :=signature.MarshalBinary() + + if err!=nil{ + return xerrors.Errorf("failed to marshal signature: %v", err) + } + + + -func sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64) { response := ptypes.TransactionInfo{ + Status: status, TransactionID: txnID, LastBlockIdx: lastBlockIdx, + Time: time, + Hash: finalHash, + Signature: signatureBin, + } - // sign the response w.Header().Set("Content-Type", "application/json") - err := json.NewEncoder(w).Encode(response) + err = json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, "failed to write in ResponseWriter: "+err.Error(), http.StatusInternalServerError) - return + return nil } + return nil + } diff --git a/proxy/types/election.go b/proxy/types/election.go index 2974990cb..051d0d40c 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -4,6 +4,22 @@ import ( etypes "github.com/dedis/d-voting/contracts/evoting/types" ) +// TransactionStatus is the status of a transaction +type TransactionStatus byte + +const ( + // UnknownTransactionStatus is the basic status of a transaction + UnknownTransactionStatus TransactionStatus = 0 + // IncludedTransaction is the status of a transaction that has been included + IncludedTransaction TransactionStatus = 1 + // RejectedTransaction is the status of a transaction that is not included and will never be + RejectedTransaction TransactionStatus = 2 +) + + + + + // CreateFormRequest defines the HTTP request for creating a form type CreateFormRequest struct { AdminID string @@ -34,10 +50,16 @@ type EGPairJSON struct { // TransactionInfo defines the HTTP response when sending a transaction to the blockchain type TransactionInfo struct { + Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected TransactionID []byte LastBlockIdx uint64 // last block of the chain when the transaction was added to the pool + Time int64 // time when the transaction was added to the pool + Hash []byte // signature of the transaction + Signature []byte // signature of the transaction } + + // UpdateFormRequest defines the HTTP request for updating a form type UpdateFormRequest struct { Action string From d9c2d17f06ee7f709a532ee43754512a8d955779 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 28 Nov 2022 16:08:26 +0100 Subject: [PATCH 18/55] Almost working Election creation --- contracts/evoting/controller/action.go | 3 +- proxy/election.go | 135 +++++++----------- proxy/mod.go | 2 +- proxy/types/election.go | 1 + web/backend/src/Server.ts | 3 + .../src/components/utils/Endpoints.tsx | 1 + .../src/pages/form/components/FormForm.tsx | 28 +++- .../form/components/utils/TransactionPoll.ts | 50 +++++++ 8 files changed, 130 insertions(+), 93 deletions(-) create mode 100644 web/frontend/src/pages/form/components/utils/TransactionPoll.ts diff --git a/contracts/evoting/controller/action.go b/contracts/evoting/controller/action.go index 9bf33cd3e..067b3ab31 100644 --- a/contracts/evoting/controller/action.go +++ b/contracts/evoting/controller/action.go @@ -169,6 +169,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { router := mux.NewRouter() router.HandleFunc(formPath, ep.NewForm).Methods("POST") + router.HandleFunc("/evoting/transactions", ep.IsTxnIncluded).Methods("GET") router.HandleFunc(formPath, ep.Forms).Methods("GET") router.HandleFunc(formPath, eproxy.AllowCORS).Methods("OPTIONS") router.HandleFunc(formIDPath, ep.Form).Methods("GET") @@ -176,13 +177,13 @@ func (a *RegisterAction) Execute(ctx node.Context) error { router.HandleFunc(formIDPath, eproxy.AllowCORS).Methods("OPTIONS") router.HandleFunc(formIDPath, ep.DeleteForm).Methods("DELETE") router.HandleFunc(formIDPath+"/vote", ep.NewFormVote).Methods("POST") - router.HandleFunc(formPath+"/transactions/{txnID}", ep.IsTxnIncluded).Methods("GET") router.NotFoundHandler = http.HandlerFunc(eproxy.NotFoundHandler) router.MethodNotAllowedHandler = http.HandlerFunc(eproxy.NotAllowedHandler) proxy.RegisterHandler(formPath, router.ServeHTTP) proxy.RegisterHandler(formPathSlash, router.ServeHTTP) + proxy.RegisterHandler("/evoting/transactions", router.ServeHTTP) dela.Logger.Info().Msg("d-voting proxy handlers registered") diff --git a/proxy/election.go b/proxy/election.go index 99fc4990e..057f349e2 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -21,9 +21,9 @@ import ( "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/pool" + "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/sign/schnorr" @@ -110,7 +110,7 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txnID, _, err := h.submitTxn(r.Context(), evoting.CmdCreateForm, evoting.FormArg, data) + txnID, blockIdx, err := h.submitTxn(r.Context(), evoting.CmdCreateForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return @@ -123,19 +123,22 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { hash.Write(txnID) formID := hash.Sum(nil) + + transactionInfo, err :=h.CreateTransactionInfo(txnID, blockIdx, ptypes.UnknownTransactionStatus) + if err != nil { + http.Error(w, "failed to create transaction info: "+err.Error(), http.StatusInternalServerError) + return + } + // return the formID response := ptypes.CreateFormResponse{ FormID: hex.EncodeToString(formID), + TransactionInfo : transactionInfo, + } // sign the response - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, "failed to write in ResponseWriter: "+err.Error(), - http.StatusInternalServerError) - return - } + sendResponse(w, response) } // NewFormVote implements proxy.Proxy @@ -455,14 +458,9 @@ func (h *form) Form(w http.ResponseWriter, r *http.Request) { Voters: form.Suffragia.UserIDs, } - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, "failed to write in ResponseWriter: "+err.Error(), - http.StatusInternalServerError) - return - } + sendResponse(w, response) + } // Forms implements proxy.Proxy. The request should not be signed because it @@ -509,13 +507,8 @@ func (h *form) Forms(w http.ResponseWriter, r *http.Request) { response := ptypes.GetFormsResponse{Forms: allFormsInfo} - w.Header().Set("Content-Type", "application/json") + sendResponse(w, response) - err = json.NewEncoder(w).Encode(response) - if err != nil { - InternalError(w, r, xerrors.Errorf("failed to write response: %v", err), nil) - return - } } // DeleteForm implements proxy.Proxy @@ -582,70 +575,30 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { //IsTxnIncluded func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { - // fetch the transactionID and block link - vars := mux.Vars(r) - - // check if the all the parameters from the TransactionInfo struct are present - if vars == nil || vars["Status"] == "" || vars["transactionID"] == "" || vars["LastBlockIdx"] == "" || vars["Time"] == "" || vars["Hash"] == "" || vars["Signature"] == "" { - http.Error(w, fmt.Sprintf("transactionID, LastBlockIdx, Time or Hash not found: %v", vars ), http.StatusInternalServerError) - return - } - - // get the status of the transaction as byte - var status ptypes.TransactionStatus - switch vars["Status"] { - case "0": - status = ptypes.UnknownTransactionStatus - case "1": // Accepted - http.Error(w, fmt.Sprintf("transaction already included: %v", vars ), http.StatusInternalServerError) - case "2": // Rejected - http.Error(w, fmt.Sprintf("transaction won't be included: %v", vars ), http.StatusInternalServerError) - - default: - http.Error(w, fmt.Sprintf("transaction status not found: %v", vars["Status"]), http.StatusInternalServerError) - return - } + var content ptypes.TransactionInfo + - // get the transactionID as []byte - transactionID, err := hex.DecodeString(vars["transactionID"]) + err := json.NewDecoder(r.Body).Decode(&content) if err != nil { - http.Error(w, fmt.Sprintf("failed to decode transactionID: %v", err), http.StatusInternalServerError) + http.Error(w, "failed to decode request: "+err.Error(), http.StatusBadRequest) return } - // get the LastBlockIdx as uint64 - lastBlockIdx, err := strconv.ParseUint(vars["LastBlockIdx"], 10, 64) - - if err != nil { - http.Error(w, fmt.Sprintf("failed to parse LastBlockIdx: %v", err), http.StatusInternalServerError) - return - } - // get the Time as int64 - transactionTime, err := strconv.ParseInt(vars["Time"], 10, 64) - if err != nil { - http.Error(w, fmt.Sprintf("failed to parse Time: %v", err), http.StatusInternalServerError) + // get the status of the transaction as byte + if content.Status != ptypes.UnknownTransactionStatus { + http.Error(w, "the transaction status is not unknown", http.StatusBadRequest) return } - // get the Hash as []byte - hash, err := hex.DecodeString(vars["Hash"]) - if err != nil { - http.Error(w, fmt.Sprintf("failed to decode Hash: %v", err), http.StatusInternalServerError) - return - } - // get the Signature as []byte - signatureBin, err := hex.DecodeString(vars["Signature"]) - if err != nil { - http.Error(w, fmt.Sprintf("failed to decode Signature: %v", err), http.StatusInternalServerError) - return - } - signature,err := h.signer.GetSignatureFactory().SignatureOf(h.context, signatureBin) + + + signature,err := h.signer.GetSignatureFactory().SignatureOf(h.context, content.Signature) if err != nil { http.Error(w, fmt.Sprintf("failed to get Signature: %v", err), http.StatusInternalServerError) return @@ -653,33 +606,33 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { // check if the hash is valid - if !h.checkHash(status, transactionID, lastBlockIdx, transactionTime, hash) { + if !h.checkHash(content.Status, content.TransactionID, content.LastBlockIdx, content.Time, content.Hash) { http.Error(w, "invalid hash", http.StatusInternalServerError) return } // check if the signature is valid - if !h.checkSignature(hash, signature) { + if !h.checkSignature(content.Hash, signature) { http.Error(w, "invalid signature", http.StatusInternalServerError) return } // check if if was submited not to long ago - if time.Now().Unix() - transactionTime > int64(maxTimeTransactionCheck) { + if time.Now().Unix() - content.Time > int64(maxTimeTransactionCheck) { http.Error(w, "the transaction is too old", http.StatusInternalServerError) return } - if time.Now().Unix() - transactionTime < 0 { + if time.Now().Unix() - content.Time < 0 { http.Error(w, "the transaction is from the future", http.StatusInternalServerError) return } // check if the transaction is included in the blockchain - newStatus, idx := h.checkTxnIncluded(transactionID, lastBlockIdx) + newStatus, idx := h.checkTxnIncluded(content.TransactionID, content.LastBlockIdx) - err = h.sendTransactionInfo(w, transactionID, idx,newStatus) + err = h.sendTransactionInfo(w, content.TransactionID, idx,newStatus) if err != nil { http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) return @@ -822,7 +775,7 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, return form, nil } -// submitTxn submits a transaction and waits for it to be included. +// submitTxn submits a transaction // Returns the transaction ID. func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, cmdArg string, payload []byte) ([]byte, uint64, error) { @@ -891,6 +844,17 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (error) { + response,err := h.CreateTransactionInfo(txnID, lastBlockIdx, status) + if err != nil { + return xerrors.Errorf("failed to create transaction info: %v", err) + } + + return sendResponse(w, response) + +} + +func (h *form) CreateTransactionInfo( txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (ptypes.TransactionInfo,error) { + time:=time.Now().Unix() hash:=sha256.New() @@ -905,18 +869,16 @@ func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBloc signature,err:=h.signer.Sign(finalHash) if err!=nil{ - return xerrors.Errorf("failed to sign transaction info: %v", err) + return ptypes.TransactionInfo{} , xerrors.Errorf("failed to sign transaction info: %v", err) } //convert signature to []byte signatureBin,err :=signature.MarshalBinary() if err!=nil{ - return xerrors.Errorf("failed to marshal signature: %v", err) + return ptypes.TransactionInfo{} , xerrors.Errorf("failed to marshal signature: %v", err) } - - response := ptypes.TransactionInfo{ Status: status, TransactionID: txnID, @@ -927,8 +889,12 @@ func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBloc } + return response,nil +} + +func sendResponse(w http.ResponseWriter, response any) (error){ w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) + err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, "failed to write in ResponseWriter: "+err.Error(), http.StatusInternalServerError) @@ -936,7 +902,6 @@ func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBloc } return nil - } diff --git a/proxy/mod.go b/proxy/mod.go index f480aa505..1c02ad38f 100644 --- a/proxy/mod.go +++ b/proxy/mod.go @@ -34,7 +34,7 @@ type Form interface { Form(http.ResponseWriter, *http.Request) // DELETE /forms/{formID} DeleteForm(http.ResponseWriter, *http.Request) - // GET /forms/transactions/{txnID} + // GET /transactions IsTxnIncluded(http.ResponseWriter, *http.Request) } diff --git a/proxy/types/election.go b/proxy/types/election.go index 051d0d40c..d27b3be9f 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -29,6 +29,7 @@ type CreateFormRequest struct { // CreateFormResponse defines the HTTP response when creating a form type CreateFormResponse struct { FormID string // hex-encoded + TransactionInfo TransactionInfo } // CastVoteRequest defines the HTTP request for casting a vote diff --git a/web/backend/src/Server.ts b/web/backend/src/Server.ts index 79f2c3876..de5ded69c 100644 --- a/web/backend/src/Server.ts +++ b/web/backend/src/Server.ts @@ -551,6 +551,9 @@ app.use('/api/evoting/*', (req, res) => { sendToDela(dataStr, req, res); }); + + + // Handles any requests that don't match the ones above app.get('*', (req, res) => { console.log('404 not found'); diff --git a/web/frontend/src/components/utils/Endpoints.tsx b/web/frontend/src/components/utils/Endpoints.tsx index 5cb7b961a..6672cf59d 100644 --- a/web/frontend/src/components/utils/Endpoints.tsx +++ b/web/frontend/src/components/utils/Endpoints.tsx @@ -5,6 +5,7 @@ export const ENDPOINT_LOGOUT = '/api/logout'; export const ENDPOINT_USER_RIGHTS = '/api/user_rights'; export const ENDPOINT_ADD_ROLE = '/api/add_role'; export const ENDPOINT_REMOVE_ROLE = '/api/remove_role'; +export const checkTransaction = '/api/evoting/transactions'; export const newForm = '/api/evoting/forms'; export const editForm = (FormID: string) => `/api/evoting/forms/${FormID}`; diff --git a/web/frontend/src/pages/form/components/FormForm.tsx b/web/frontend/src/pages/form/components/FormForm.tsx index 45ed28deb..d8e523091 100644 --- a/web/frontend/src/pages/form/components/FormForm.tsx +++ b/web/frontend/src/pages/form/components/FormForm.tsx @@ -1,11 +1,12 @@ import { FC, Fragment, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { newForm } from 'components/utils/Endpoints'; +import { checkTransaction, newForm } from 'components/utils/Endpoints'; import { CloudUploadIcon, PencilIcon, TrashIcon } from '@heroicons/react/solid'; import SubjectComponent from './SubjectComponent'; import UploadFile from './UploadFile'; +import pollTransaction from './utils/TransactionPoll'; import configurationSchema from '../../../schema/configurationValidation'; import { Configuration, ID, Subject } from '../../../types/configuration'; @@ -78,12 +79,27 @@ const FormForm: FC = () => { setShowModal(true); } else { const response = await res.json(); - setNavigateDestination('/forms/' + response.FormID); - setTextModal(`${t('successCreateForm')} ${response.FormID}`); - setShowModal(true); - setConf(emptyConf); + // TODO: use the new transaction system + + console.log(response); + + console.log(response.TransactionInfo); + + pollTransaction(checkTransaction, response.TransactionInfo, 1000, 30).then( + () => { + setNavigateDestination('/forms/' + response.FormID); + setTextModal(`${t('successCreateForm')} ${response.FormID}`); + setShowModal(true); + setConf(emptyConf); + setLoading(false); + }, + (err) => { + setTextModal(err); + setShowModal(true); + setLoading(false); + } + ); } - setLoading(false); } catch (error) { setTextModal(error.message); setShowModal(true); diff --git a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts new file mode 100644 index 000000000..b32d7b554 --- /dev/null +++ b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts @@ -0,0 +1,50 @@ +const pollTransaction = ( + endpoint: RequestInfo, + data: any, + interval: number, + maxAttempts: number +) => { + let attempts = 0; + + const request = { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + data: data, + }; + + const executePoll = async (resolve, reject) => { + try { + attempts += 1; + console.log('Request:' + JSON.stringify(request)); + const response = await fetch(endpoint, request); + const result = await response.json(); + + if (!response.ok) { + throw new Error(JSON.stringify(result)); + } + + request.data = result; + + if (result.Status === '1') { + return resolve(result); + } + + if (result.Status === '2') { + throw new Error('Transaction Rejected'); + } + + // Add a timeout + if (attempts === maxAttempts) { + throw new Error('Timeout'); + } + + setTimeout(executePoll, interval, resolve, reject); + } catch (e) { + return reject(e); + } + }; + + return new Promise(executePoll); +}; + +export default pollTransaction; From 1c5b4d7f3f91453fa2e41867772e5bd5e54a820c Mon Sep 17 00:00:00 2001 From: A Date: Wed, 30 Nov 2022 14:47:15 +0100 Subject: [PATCH 19/55] small edit in load_test --- integration/load_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/integration/load_test.go b/integration/load_test.go index 3eed0de2f..fe89a203a 100644 --- a/integration/load_test.go +++ b/integration/load_test.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "math/rand" "net/http" "os" @@ -411,11 +410,9 @@ func cast(isRetry bool, castVoteRequest ptypes.CastVoteRequest, contentType, ran return } - responseBody,err:=ioutil.ReadAll(resp.Body) - require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s %s", resp.Status,responseBody) - - _, err = io.ReadAll(resp.Body) + responseBody,err:=io.ReadAll(resp.Body) require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s %s", resp.Status,responseBody) resp.Body.Close() } From e1a2d7a76d7335c05a7db9d7b28d4499a88a0fb4 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:14:53 +0100 Subject: [PATCH 20/55] refactor integration test utility functions Removed AddNodeTest (not working) which will be added to another branch --- integration/integration_test.go | 153 -------------------------------- 1 file changed, 153 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index 3274ee767..779c51641 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -16,8 +16,6 @@ import ( delaPkg "go.dedis.ch/dela" ) - - // Check the shuffled votes versus the cast votes on a few nodes func TestIntegration(t *testing.T) { t.Run("3 nodes, 3 votes", getIntegrationTest(3, 3)) @@ -30,10 +28,6 @@ func TestCrash(t *testing.T) { t.Run("10 nodes, 5 votes, 3 fails", getIntegrationTestCrash(10, 5, 3)) } -func TestAddNodes(t *testing.T) { - t.Run("5 nodes, 10 votes add 2 node during the process", getIntegrationTestAddNodes(5, 10, 2)) -} - func BenchmarkIntegration(b *testing.B) { b.Run("10 nodes, 100 votes", getIntegrationBenchmark(10, 100)) } @@ -341,150 +335,6 @@ func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing } } -func getIntegrationTestAddNodes(numNodes, numVotes, numNewNodes int) func(*testing.T) { - return func(t *testing.T) { - t.Parallel() - - adminID := "first admin" - - // ##### SETUP ENV ##### - // make tests reproducible - rand.Seed(1) - - delaPkg.Logger = delaPkg.Logger.Level(zerolog.WarnLevel) - - dirPath, err := os.MkdirTemp(os.TempDir(), "d-voting-three-votes") - require.NoError(t, err) - - defer os.RemoveAll(dirPath) - - t.Logf("using temp dir %s", dirPath) - - // ##### CREATE NODES ##### - nodes := setupDVotingNodes(t, numNodes, dirPath, nil) - - signer := createDVotingAccess(t, nodes, dirPath) - - m := newTxManager(signer, nodes[0], time.Second*time.Duration(numNodes/2+1), numNodes*4) - - err = grantAccess(m, signer) - require.NoError(t, err) - - for _, n := range nodes { - err = grantAccess(m, n.GetShuffleSigner()) - require.NoError(t, err) - } - - // ##### CREATE FORM ##### - formID, err := createForm(m, "Three votes form", adminID) - require.NoError(t, err) - - time.Sleep(time.Second * 1) - - // ##### SETUP DKG ##### - actor, err := initDkg(nodes, formID, m.m) - require.NoError(t, err) - - // ##### OPEN FORM ##### - err = openForm(m, formID) - require.NoError(t, err) - - formFac := types.NewFormFactory(types.CiphervoteFactory{}, nodes[0].GetRosterFac()) - - t.Logf("start casting votes") - form, err := getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - - newNodes := setupDVotingNodes(t, numNewNodes, dirPath, nodes[0]) - nodes = append(nodes, newNodes...) - - castedVotes, err := castVotesRandomly(m, actor, form, numVotes) - require.NoError(t, err) - - fmt.Println("casted votes:", castedVotes) - - // ##### CLOSE FORM ##### - err = closeForm(m, formID, adminID) - require.NoError(t, err) - - err = waitForStatus(types.Closed, formFac, formID, nodes, numNodes, - 5*time.Second) - require.NoError(t, err) - - // ##### SHUFFLE BALLOTS ##### - t.Logf("initializing shuffle") - sActor, err := initShuffle(nodes) - require.NoError(t, err) - - time.Sleep(time.Second * 1) - - t.Logf("shuffling") - err = sActor.Shuffle(formID) - require.NoError(t, err) - - err = waitForStatus(types.ShuffledBallots, formFac, formID, nodes, - numNodes, 2*time.Second*time.Duration(numNodes)) - require.NoError(t, err) - - // ##### SUBMIT PUBLIC SHARES ##### - t.Logf("submitting public shares") - - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - err = actor.ComputePubshares() - require.NoError(t, err) - - err = waitForStatus(types.PubSharesSubmitted, formFac, formID, nodes, - numNodes, 6*time.Second*time.Duration(numNodes)) - require.NoError(t, err) - - // ##### DECRYPT BALLOTS ##### - t.Logf("decrypting") - - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - t.Logf("PubsharesUnit: %v", form.PubsharesUnits) - require.NoError(t, err) - err = decryptBallots(m, actor, form) - require.NoError(t, err) - - err = waitForStatus(types.ResultAvailable, formFac, formID, nodes, - numNodes, 1500*time.Millisecond*time.Duration(numVotes)) - require.NoError(t, err) - - t.Logf("get vote proof") - form, err = getForm(formFac, formID, nodes[0].GetOrdering()) - require.NoError(t, err) - - fmt.Println("Title of the form : " + form.Configuration.MainTitle) - fmt.Println("ID of the form : " + string(form.FormID)) - fmt.Println("Status of the form : " + strconv.Itoa(int(form.Status))) - fmt.Println("Number of decrypted ballots : " + strconv.Itoa(len(form.DecryptedBallots))) - - require.Len(t, form.DecryptedBallots, len(castedVotes)) - - for _, b := range form.DecryptedBallots { - ok := false - for i, casted := range castedVotes { - if b.Equal(casted) { - //remove the casted vote from the list - castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) - ok = true - break - } - } - require.True(t, ok) - } - require.Empty(t, castedVotes) - - fmt.Println("closing nodes") - - err = closeNodes(nodes) - require.NoError(t, err) - - fmt.Println("test done") - } -} - func getIntegrationBenchmark(numNodes, numVotes int) func(*testing.B) { return func(b *testing.B) { @@ -616,6 +466,3 @@ func getIntegrationBenchmark(numNodes, numVotes int) func(*testing.B) { fmt.Println("test done") } } - -// ----------------------------------------------------------------------------- -// Utility functions From b464bc3bd80025ecadd4e7b61f5263dd6b056a0f Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:15:30 +0100 Subject: [PATCH 21/55] add new files --- integration/dkg_utility_functions.go | 29 +++ ...functions.go => form_utility_functions.go} | 223 +----------------- integration/nodes_utility_functions.go | 76 ++++++ integration/shuffle_utility_functions.go | 28 +++ integration/transaction_utility_functions.go | 115 +++++++++ 5 files changed, 252 insertions(+), 219 deletions(-) create mode 100644 integration/dkg_utility_functions.go rename integration/{utility_functions.go => form_utility_functions.go} (61%) create mode 100644 integration/nodes_utility_functions.go create mode 100644 integration/shuffle_utility_functions.go create mode 100644 integration/transaction_utility_functions.go diff --git a/integration/dkg_utility_functions.go b/integration/dkg_utility_functions.go new file mode 100644 index 000000000..8a583fa19 --- /dev/null +++ b/integration/dkg_utility_functions.go @@ -0,0 +1,29 @@ +package integration + +import ( + "github.com/dedis/d-voting/services/dkg" + "go.dedis.ch/dela/core/txn" + "golang.org/x/xerrors" +) + +func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { + var actor dkg.Actor + var err error + + for _, node := range nodes { + d := node.(dVotingNode).GetDkg() + + // put Listen in a goroutine to optimize for speed + actor, err = d.Listen(formID, m) + if err != nil { + return nil, xerrors.Errorf("failed to GetDkg: %v", err) + } + } + + _, err = actor.Setup() + if err != nil { + return nil, xerrors.Errorf("failed to Setup: %v", err) + } + + return actor, nil +} diff --git a/integration/utility_functions.go b/integration/form_utility_functions.go similarity index 61% rename from integration/utility_functions.go rename to integration/form_utility_functions.go index 43bf862d8..87029942a 100644 --- a/integration/utility_functions.go +++ b/integration/form_utility_functions.go @@ -1,8 +1,6 @@ package integration import ( - "bytes" - "context" "crypto/sha256" "encoding/base64" "encoding/hex" @@ -10,132 +8,32 @@ import ( "math/rand" "strconv" "strings" - "sync" - "time" "github.com/dedis/d-voting/contracts/evoting" "github.com/dedis/d-voting/contracts/evoting/types" "github.com/dedis/d-voting/internal/testing/fake" "github.com/dedis/d-voting/services/dkg" - "github.com/dedis/d-voting/services/shuffle" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/serde" "go.dedis.ch/dela/serde/json" "go.dedis.ch/kyber/v3" "golang.org/x/xerrors" ) -const addAndWaitErr = "failed to addAndWait: %v" - var serdecontext = json.NewContext() +func encodeID(ID string) types.ID { + return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) +} + func ballotIsNull(ballot types.Ballot) bool { return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && ballot.RankResultIDs == nil && ballot.RankResult == nil && ballot.TextResultIDs == nil && ballot.TextResult == nil } -func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, - timeout time.Duration, retry int) txManager { - - client := client{ - srvc: firstNode.GetOrdering(), - mgr: firstNode.GetValidationSrv(), - } - - return txManager{ - m: signed.NewManager(signer, client), - n: firstNode, - t: timeout, - retry: retry, - } -} - -type txManager struct { - m txn.Manager - n dVotingCosiDela - t time.Duration - retry int -} - -func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { - for i := 0; i < m.retry; i++ { - sentTxn, err := m.m.Make(args...) - if err != nil { - return nil, xerrors.Errorf("failed to Make: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), m.t) - defer cancel() - - events := m.n.GetOrdering().Watch(ctx) - - err = m.n.GetPool().Add(sentTxn) - if err != nil { - return nil, xerrors.Errorf("failed to Add: %v", err) - } - - sentTxnID := sentTxn.GetID() - - accepted := isAccepted(events, sentTxnID) - if accepted { - return sentTxnID, nil - } - - err = m.m.Sync() - if err != nil { - return nil, xerrors.Errorf("failed to sync: %v", err) - } - - cancel() - } - - return nil, xerrors.Errorf("transaction not included after timeout: %v", args) -} - -// isAccepted returns true if the transaction was included then accepted -func isAccepted(events <-chan ordering.Event, txID []byte) bool { - for event := range events { - for _, result := range event.Transactions { - fetchedTxnID := result.GetTransaction().GetID() - - if bytes.Equal(txID, fetchedTxnID) { - accepted, _ := event.Transactions[0].GetStatus() - - return accepted - } - } - } - - return false -} - -func grantAccess(m txManager, signer crypto.Signer) error { - pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to GetPublicKey: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, - {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, - {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, - {Key: "access:grant_command", Value: []byte("all")}, - {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, - {Key: "access:command", Value: []byte("GRANT")}, - } - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf("failed to grantAccess: %v", err) - } - - return nil -} - func createForm(m txManager, title string, admin string) ([]byte, error) { // Define the configuration : configuration := fake.BasicConfiguration @@ -382,49 +280,6 @@ func closeForm(m txManager, formID []byte, admin string) error { return nil } -func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { - var actor dkg.Actor - var err error - - for _, node := range nodes { - d := node.(dVotingNode).GetDkg() - - // put Listen in a goroutine to optimize for speed - actor, err = d.Listen(formID, m) - if err != nil { - return nil, xerrors.Errorf("failed to GetDkg: %v", err) - } - } - - _, err = actor.Setup() - if err != nil { - return nil, xerrors.Errorf("failed to Setup: %v", err) - } - - return actor, nil -} - -func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { - var sActor shuffle.Actor - - for _, node := range nodes { - client := client{ - srvc: node.GetOrdering(), - mgr: node.GetValidationSrv(), - } - - var err error - shuffler := node.GetShuffle() - - sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) - if err != nil { - return nil, xerrors.Errorf("failed to init Shuffle: %v", err) - } - } - - return sActor, nil -} - func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { if form.Status != types.PubSharesSubmitted { return xerrors.Errorf("cannot decrypt: not all pubShares submitted") @@ -452,73 +307,3 @@ func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { return nil } - -func closeNodes(nodes []dVotingCosiDela) error { - wait := sync.WaitGroup{} - wait.Add(len(nodes)) - - for _, n := range nodes { - go func(node dVotingNode) { - defer wait.Done() - node.GetOrdering().Close() - }(n.(dVotingNode)) - } - - done := make(chan struct{}) - - go func() { - wait.Wait() - close(done) - }() - - select { - case <-done: - return nil - case <-time.After(time.Second * 30): - return xerrors.New("failed to close: timeout") - } -} - -func encodeID(ID string) types.ID { - return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) -} - -// waitForStatus polls the nodes until they all updated to the expected status -// for the given form. An error is raised if the timeout expires. -func waitForStatus(status types.Status, formFac types.FormFactory, - formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { - - expiration := time.Now().Add(timeOut) - - isOK := func() (bool, error) { - for _, node := range nodes { - form, err := getForm(formFac, formID, node.GetOrdering()) - if err != nil { - return false, xerrors.Errorf("failed to get form: %v", err) - } - - if form.Status != status { - return false, nil - } - } - - return true, nil - } - - for { - if time.Now().After(expiration) { - return xerrors.New("status check expired") - } - - ok, err := isOK() - if err != nil { - return xerrors.Errorf("failed to check status: %v", err) - } - - if ok { - return nil - } - - time.Sleep(time.Millisecond * 100) - } -} diff --git a/integration/nodes_utility_functions.go b/integration/nodes_utility_functions.go new file mode 100644 index 000000000..b1b62313d --- /dev/null +++ b/integration/nodes_utility_functions.go @@ -0,0 +1,76 @@ +package integration + +import ( + "sync" + "time" + + "github.com/dedis/d-voting/contracts/evoting/types" + "golang.org/x/xerrors" +) + +func closeNodes(nodes []dVotingCosiDela) error { + wait := sync.WaitGroup{} + wait.Add(len(nodes)) + + for _, n := range nodes { + go func(node dVotingNode) { + defer wait.Done() + node.GetOrdering().Close() + }(n.(dVotingNode)) + } + + done := make(chan struct{}) + + go func() { + wait.Wait() + close(done) + }() + + select { + case <-done: + return nil + case <-time.After(time.Second * 30): + return xerrors.New("failed to close: timeout") + } +} + + +// waitForStatus polls the nodes until they all updated to the expected status +// for the given form. An error is raised if the timeout expires. +func waitForStatus(status types.Status, formFac types.FormFactory, + formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { + + expiration := time.Now().Add(timeOut) + + isOK := func() (bool, error) { + for _, node := range nodes { + form, err := getForm(formFac, formID, node.GetOrdering()) + if err != nil { + return false, xerrors.Errorf("failed to get form: %v", err) + } + + if form.Status != status { + return false, nil + } + } + + return true, nil + } + + for { + if time.Now().After(expiration) { + return xerrors.New("status check expired") + } + + ok, err := isOK() + if err != nil { + return xerrors.Errorf("failed to check status: %v", err) + } + + if ok { + return nil + } + + time.Sleep(time.Millisecond * 100) + } +} diff --git a/integration/shuffle_utility_functions.go b/integration/shuffle_utility_functions.go new file mode 100644 index 000000000..956dc1505 --- /dev/null +++ b/integration/shuffle_utility_functions.go @@ -0,0 +1,28 @@ +package integration + +import ( + "github.com/dedis/d-voting/services/shuffle" + "go.dedis.ch/dela/core/txn/signed" + "golang.org/x/xerrors" +) + +func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { + var sActor shuffle.Actor + + for _, node := range nodes { + client := client{ + srvc: node.GetOrdering(), + mgr: node.GetValidationSrv(), + } + + var err error + shuffler := node.GetShuffle() + + sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) + if err != nil { + return nil, xerrors.Errorf("failed to init Shuffle: %v", err) + } + } + + return sActor, nil +} diff --git a/integration/transaction_utility_functions.go b/integration/transaction_utility_functions.go new file mode 100644 index 000000000..12a0aa3bb --- /dev/null +++ b/integration/transaction_utility_functions.go @@ -0,0 +1,115 @@ +package integration + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/hex" + "time" + + "go.dedis.ch/dela/core/execution/native" + "go.dedis.ch/dela/core/ordering" + "go.dedis.ch/dela/core/txn" + "go.dedis.ch/dela/core/txn/signed" + "go.dedis.ch/dela/crypto" + "golang.org/x/xerrors" +) + +const addAndWaitErr = "failed to addAndWait: %v" + +func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, + timeout time.Duration, retry int) txManager { + + client := client{ + srvc: firstNode.GetOrdering(), + mgr: firstNode.GetValidationSrv(), + } + + return txManager{ + m: signed.NewManager(signer, client), + n: firstNode, + t: timeout, + retry: retry, + } +} + +type txManager struct { + m txn.Manager + n dVotingCosiDela + t time.Duration + retry int +} + +func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { + for i := 0; i < m.retry; i++ { + sentTxn, err := m.m.Make(args...) + if err != nil { + return nil, xerrors.Errorf("failed to Make: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), m.t) + defer cancel() + + events := m.n.GetOrdering().Watch(ctx) + + err = m.n.GetPool().Add(sentTxn) + if err != nil { + return nil, xerrors.Errorf("failed to Add: %v", err) + } + + sentTxnID := sentTxn.GetID() + + accepted := isAccepted(events, sentTxnID) + if accepted { + return sentTxnID, nil + } + + err = m.m.Sync() + if err != nil { + return nil, xerrors.Errorf("failed to sync: %v", err) + } + + cancel() + } + + return nil, xerrors.Errorf("transaction not included after timeout: %v", args) +} + +// isAccepted returns true if the transaction was included then accepted +func isAccepted(events <-chan ordering.Event, txID []byte) bool { + for event := range events { + for _, result := range event.Transactions { + fetchedTxnID := result.GetTransaction().GetID() + + if bytes.Equal(txID, fetchedTxnID) { + accepted, _ := event.Transactions[0].GetStatus() + + return accepted + } + } + } + + return false +} + +func grantAccess(m txManager, signer crypto.Signer) error { + pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() + if err != nil { + return xerrors.Errorf("failed to GetPublicKey: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, + {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, + {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, + {Key: "access:grant_command", Value: []byte("all")}, + {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, + {Key: "access:command", Value: []byte("GRANT")}, + } + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf("failed to grantAccess: %v", err) + } + + return nil +} From e68134eccee0916c86cda75a4093ea0f64ca06b1 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:48:16 +0100 Subject: [PATCH 22/55] removed an integration test too long for github Added continuous integration for new tests --- .github/workflows/go_integration_tests.yml | 13 ++++++++++++- integration/integration_test.go | 5 ++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index d0829b2b5..443a1c197 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -21,4 +21,15 @@ jobs: uses: actions/checkout@v2 - name: Run the integration test - run: go test -timeout 15m -run TestIntegration ./integration/... \ No newline at end of file + run: go test -timeout 10m -run TestIntegration ./integration/... + + - name: Run the crash test + run: go test -timeout 10m -run TestCrash ./integration/... + + - name: Run the revote test + run: go test -timeout 10m -run TestRevote ./integration/... + + - name: Run the bad vote test + run: go test -timeout 10m -run TestBadVote ./integration/... + + \ No newline at end of file diff --git a/integration/integration_test.go b/integration/integration_test.go index 779c51641..01b5b60c6 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -18,14 +18,13 @@ import ( // Check the shuffled votes versus the cast votes on a few nodes func TestIntegration(t *testing.T) { - t.Run("3 nodes, 3 votes", getIntegrationTest(3, 3)) - t.Run("10 nodes, 100 votes", getIntegrationTest(10, 100)) + t.Run("3 nodes, 3 votes", getIntegrationTest(4, 5)) + } func TestCrash(t *testing.T) { t.Run("5 nodes, 5 votes, 1 fail", getIntegrationTestCrash(5, 5, 1)) t.Run("5 nodes, 5 votes, 2 fails", getIntegrationTestCrash(5, 5, 2)) - t.Run("10 nodes, 5 votes, 3 fails", getIntegrationTestCrash(10, 5, 3)) } func BenchmarkIntegration(b *testing.B) { From 388d3caa3f8478909b53498cf5e60caba170fbc6 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:53:08 +0100 Subject: [PATCH 23/55] Refactor gitHub actions --- .github/workflows/go_bad_vote_test.yml | 26 +++++++++++++++++++++ .github/workflows/go_integration_tests.yml | 10 -------- .github/workflows/go_node_crashing_test.yml | 26 +++++++++++++++++++++ .github/workflows/go_revote_test.yml | 26 +++++++++++++++++++++ 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/go_bad_vote_test.yml create mode 100644 .github/workflows/go_node_crashing_test.yml create mode 100644 .github/workflows/go_revote_test.yml diff --git a/.github/workflows/go_bad_vote_test.yml b/.github/workflows/go_bad_vote_test.yml new file mode 100644 index 000000000..0253c41f6 --- /dev/null +++ b/.github/workflows/go_bad_vote_test.yml @@ -0,0 +1,26 @@ +name: Go Integration Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the bad vote test + run: go test -timeout 10m -run TestBadVote ./integration/... + + \ No newline at end of file diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 443a1c197..a6b5969fd 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -22,14 +22,4 @@ jobs: - name: Run the integration test run: go test -timeout 10m -run TestIntegration ./integration/... - - - name: Run the crash test - run: go test -timeout 10m -run TestCrash ./integration/... - - - name: Run the revote test - run: go test -timeout 10m -run TestRevote ./integration/... - - - name: Run the bad vote test - run: go test -timeout 10m -run TestBadVote ./integration/... - \ No newline at end of file diff --git a/.github/workflows/go_node_crashing_test.yml b/.github/workflows/go_node_crashing_test.yml new file mode 100644 index 000000000..78345fa77 --- /dev/null +++ b/.github/workflows/go_node_crashing_test.yml @@ -0,0 +1,26 @@ +name: Go Integration Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the crash test + run: go test -timeout 10m -run TestCrash ./integration/... + + \ No newline at end of file diff --git a/.github/workflows/go_revote_test.yml b/.github/workflows/go_revote_test.yml new file mode 100644 index 000000000..00c9787ab --- /dev/null +++ b/.github/workflows/go_revote_test.yml @@ -0,0 +1,26 @@ +name: Go Integration Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the revote test + run: go test -timeout 10m -run TestRevote ./integration/... + + \ No newline at end of file From 5fd305b4fe57c20d67a4e2f62928cf48208866da Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:55:08 +0100 Subject: [PATCH 24/55] renamed calls --- .github/workflows/go_bad_vote_test.yml | 2 +- .github/workflows/go_node_crashing_test.yml | 2 +- .github/workflows/go_revote_test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go_bad_vote_test.yml b/.github/workflows/go_bad_vote_test.yml index 0253c41f6..caafdaa90 100644 --- a/.github/workflows/go_bad_vote_test.yml +++ b/.github/workflows/go_bad_vote_test.yml @@ -1,4 +1,4 @@ -name: Go Integration Test +name: Go Bad Vote Test on: push: diff --git a/.github/workflows/go_node_crashing_test.yml b/.github/workflows/go_node_crashing_test.yml index 78345fa77..733bc83a0 100644 --- a/.github/workflows/go_node_crashing_test.yml +++ b/.github/workflows/go_node_crashing_test.yml @@ -1,4 +1,4 @@ -name: Go Integration Test +name: Go Node Crashing Test on: push: diff --git a/.github/workflows/go_revote_test.yml b/.github/workflows/go_revote_test.yml index 00c9787ab..7a6a2829e 100644 --- a/.github/workflows/go_revote_test.yml +++ b/.github/workflows/go_revote_test.yml @@ -1,4 +1,4 @@ -name: Go Integration Test +name: Go Revote Test on: push: From cee74c79073910f6dbb015717d050e92a84535ca Mon Sep 17 00:00:00 2001 From: A Date: Wed, 7 Dec 2022 15:03:22 +0100 Subject: [PATCH 25/55] Renamed some files, reformating Modification of github Actions --- .github/workflows/go_bad_vote_test.yml | 26 ---------- .github/workflows/go_integration_tests.yml | 52 ++++++++++++++++++- .github/workflows/go_node_crashing_test.yml | 26 ---------- .github/workflows/go_revote_test.yml | 26 ---------- .../{dkg_utility_functions.go => dkg.go} | 0 .../{form_utility_functions.go => form.go} | 0 integration/integration_test.go | 2 +- integration/load_test.go | 24 ++++----- .../{nodes_utility_functions.go => nodes.go} | 1 - integration/scenario_test.go | 2 +- ...huffle_utility_functions.go => shuffle.go} | 0 ...on_utility_functions.go => transaction.go} | 0 12 files changed, 61 insertions(+), 98 deletions(-) delete mode 100644 .github/workflows/go_bad_vote_test.yml delete mode 100644 .github/workflows/go_node_crashing_test.yml delete mode 100644 .github/workflows/go_revote_test.yml rename integration/{dkg_utility_functions.go => dkg.go} (100%) rename integration/{form_utility_functions.go => form.go} (100%) rename integration/{nodes_utility_functions.go => nodes.go} (99%) rename integration/{shuffle_utility_functions.go => shuffle.go} (100%) rename integration/{transaction_utility_functions.go => transaction.go} (100%) diff --git a/.github/workflows/go_bad_vote_test.yml b/.github/workflows/go_bad_vote_test.yml deleted file mode 100644 index caafdaa90..000000000 --- a/.github/workflows/go_bad_vote_test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Go Bad Vote Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - - test: - name: Tests - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.17 - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run the bad vote test - run: go test -timeout 10m -run TestBadVote ./integration/... - - \ No newline at end of file diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index a6b5969fd..3fccf9ab5 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -9,7 +9,7 @@ on: jobs: test: - name: Tests + name: TestIntegration runs-on: ubuntu-latest steps: - name: Set up Go ^1.17 @@ -22,4 +22,52 @@ jobs: - name: Run the integration test run: go test -timeout 10m -run TestIntegration ./integration/... - \ No newline at end of file + + + test: + name: TestBadVote + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the bad vote test + run: go test -timeout 10m -run TestBadVote ./integration/... + test: + name: TestCrash + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the crash test + run: go test -timeout 10m -run TestCrash ./integration/... + test: + name: TestRevote + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the revote test + run: go test -timeout 10m -run TestRevote ./integration/... + + + + + diff --git a/.github/workflows/go_node_crashing_test.yml b/.github/workflows/go_node_crashing_test.yml deleted file mode 100644 index 733bc83a0..000000000 --- a/.github/workflows/go_node_crashing_test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Go Node Crashing Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - - test: - name: Tests - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.17 - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run the crash test - run: go test -timeout 10m -run TestCrash ./integration/... - - \ No newline at end of file diff --git a/.github/workflows/go_revote_test.yml b/.github/workflows/go_revote_test.yml deleted file mode 100644 index 7a6a2829e..000000000 --- a/.github/workflows/go_revote_test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Go Revote Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - - test: - name: Tests - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.17 - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run the revote test - run: go test -timeout 10m -run TestRevote ./integration/... - - \ No newline at end of file diff --git a/integration/dkg_utility_functions.go b/integration/dkg.go similarity index 100% rename from integration/dkg_utility_functions.go rename to integration/dkg.go diff --git a/integration/form_utility_functions.go b/integration/form.go similarity index 100% rename from integration/form_utility_functions.go rename to integration/form.go diff --git a/integration/integration_test.go b/integration/integration_test.go index 01b5b60c6..95db01e83 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -19,7 +19,7 @@ import ( // Check the shuffled votes versus the cast votes on a few nodes func TestIntegration(t *testing.T) { t.Run("3 nodes, 3 votes", getIntegrationTest(4, 5)) - + } func TestCrash(t *testing.T) { diff --git a/integration/load_test.go b/integration/load_test.go index fe89a203a..0903cabc5 100644 --- a/integration/load_test.go +++ b/integration/load_test.go @@ -95,9 +95,7 @@ func startFormProcessLoad(wg *sync.WaitGroup, numNodes, numVotesPerSec, numSec i body, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s %s", resp.Status,body) - - + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s %s", resp.Status, body) t.Log("response body:", string(body)) resp.Body.Close() @@ -369,20 +367,19 @@ func castVotesLoad(numVotesPerSec, numSec, BallotSize, chunksPerBallot int, form for i := 0; i < numSec; i++ { // send the votes asynchrounously and wait for the response - + for j := 0; j < numVotesPerSec; j++ { randomproxy := proxyArray[rand.Intn(proxyCount)] castVoteRequest := ptypes.CastVoteRequest{ - UserID: "user"+strconv.Itoa(i*numVotesPerSec+j), + UserID: "user" + strconv.Itoa(i*numVotesPerSec+j), Ballot: ballot, } - go cast(false,castVoteRequest, contentType, randomproxy, formID, secret, t) + go cast(false, castVoteRequest, contentType, randomproxy, formID, secret, t) } t.Logf("casted votes %d", (i+1)*numVotesPerSec) time.Sleep(time.Second) - } time.Sleep(time.Second * 30) @@ -390,11 +387,8 @@ func castVotesLoad(numVotesPerSec, numSec, BallotSize, chunksPerBallot int, form return votesfrontend } +func cast(isRetry bool, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, t *testing.T) { -func cast(isRetry bool, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, t *testing.T) { - - - t.Logf("cast ballot to proxy %v", randomproxy) // t.Logf("vote is: %v", castVoteRequest) @@ -403,16 +397,16 @@ func cast(isRetry bool, castVoteRequest ptypes.CastVoteRequest, contentType, ran resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) require.NoError(t, err) - + if http.StatusOK != resp.StatusCode && !isRetry { t.Logf("unexpected status: %s retry", resp.Status) - cast(true,castVoteRequest, contentType, randomproxy, formID, secret, t) + cast(true, castVoteRequest, contentType, randomproxy, formID, secret, t) return } - responseBody,err:=io.ReadAll(resp.Body) + responseBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s %s", resp.Status,responseBody) + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s %s", resp.Status, responseBody) resp.Body.Close() } diff --git a/integration/nodes_utility_functions.go b/integration/nodes.go similarity index 99% rename from integration/nodes_utility_functions.go rename to integration/nodes.go index b1b62313d..e95190cc7 100644 --- a/integration/nodes_utility_functions.go +++ b/integration/nodes.go @@ -34,7 +34,6 @@ func closeNodes(nodes []dVotingCosiDela) error { } } - // waitForStatus polls the nodes until they all updated to the expected status // for the given form. An error is raised if the timeout expires. func waitForStatus(status types.Status, formFac types.FormFactory, diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 512186797..611aaa529 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -206,7 +206,7 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray var bMarshal types.Ballot form := types.Form{ Configuration: fakeConfiguration, - FormID: formID, + FormID: formID, BallotSize: BallotSize, } diff --git a/integration/shuffle_utility_functions.go b/integration/shuffle.go similarity index 100% rename from integration/shuffle_utility_functions.go rename to integration/shuffle.go diff --git a/integration/transaction_utility_functions.go b/integration/transaction.go similarity index 100% rename from integration/transaction_utility_functions.go rename to integration/transaction.go From 5c3562aa44450ccb10962e4797b4b4c1acaf7621 Mon Sep 17 00:00:00 2001 From: A Date: Wed, 7 Dec 2022 15:31:19 +0100 Subject: [PATCH 26/55] refactor --- .github/workflows/go_integration_tests.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 3fccf9ab5..1813401fb 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -22,8 +22,6 @@ jobs: - name: Run the integration test run: go test -timeout 10m -run TestIntegration ./integration/... - - test: name: TestBadVote runs-on: ubuntu-latest From 1cfffaf3b039e18d1b0d089ae48afcb6db7160ed Mon Sep 17 00:00:00 2001 From: A Date: Wed, 7 Dec 2022 16:06:40 +0100 Subject: [PATCH 27/55] fix github action --- .github/workflows/go_integration_tests.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 1813401fb..8bee5e554 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -8,8 +8,8 @@ on: jobs: - test: - name: TestIntegration + integration: + name: Test Integration runs-on: ubuntu-latest steps: - name: Set up Go ^1.17 @@ -22,8 +22,8 @@ jobs: - name: Run the integration test run: go test -timeout 10m -run TestIntegration ./integration/... - test: - name: TestBadVote + bad_vote: + name: Test bad vote runs-on: ubuntu-latest steps: - name: Set up Go ^1.17 @@ -36,8 +36,8 @@ jobs: - name: Run the bad vote test run: go test -timeout 10m -run TestBadVote ./integration/... - test: - name: TestCrash + crash: + name: Test crash runs-on: ubuntu-latest steps: - name: Set up Go ^1.17 @@ -50,8 +50,8 @@ jobs: - name: Run the crash test run: go test -timeout 10m -run TestCrash ./integration/... - test: - name: TestRevote + revote: + name: Test revote runs-on: ubuntu-latest steps: - name: Set up Go ^1.17 From 6ed61fbe7247eee0e367149796ed0a2c8e18d33a Mon Sep 17 00:00:00 2001 From: A Date: Thu, 8 Dec 2022 14:53:39 +0100 Subject: [PATCH 28/55] Working New transaction System --- contracts/evoting/controller/action.go | 3 +- proxy/election.go | 184 +- proxy/mod.go | 2 +- proxy/types/election.go | 11 +- runSystems.sh | 5 +- web/backend/package-lock.json | 76 +- web/backend/yarn.lock | 5777 +++++++++-------- .../src/components/utils/Endpoints.tsx | 2 +- .../src/components/utils/usePostCall.tsx | 36 +- .../src/pages/form/components/FormForm.tsx | 4 +- .../form/components/utils/TransactionPoll.ts | 14 +- 11 files changed, 3327 insertions(+), 2787 deletions(-) diff --git a/contracts/evoting/controller/action.go b/contracts/evoting/controller/action.go index 067b3ab31..b109eb53b 100644 --- a/contracts/evoting/controller/action.go +++ b/contracts/evoting/controller/action.go @@ -169,7 +169,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { router := mux.NewRouter() router.HandleFunc(formPath, ep.NewForm).Methods("POST") - router.HandleFunc("/evoting/transactions", ep.IsTxnIncluded).Methods("GET") + router.HandleFunc("/evoting/transactions/{token}", ep.IsTxnIncluded).Methods("GET") router.HandleFunc(formPath, ep.Forms).Methods("GET") router.HandleFunc(formPath, eproxy.AllowCORS).Methods("OPTIONS") router.HandleFunc(formIDPath, ep.Form).Methods("GET") @@ -184,6 +184,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error { proxy.RegisterHandler(formPath, router.ServeHTTP) proxy.RegisterHandler(formPathSlash, router.ServeHTTP) proxy.RegisterHandler("/evoting/transactions", router.ServeHTTP) + proxy.RegisterHandler("/evoting/transactions/", router.ServeHTTP) dela.Logger.Info().Msg("d-voting proxy handlers registered") diff --git a/proxy/election.go b/proxy/election.go index 057f349e2..e30cf5950 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -4,9 +4,11 @@ import ( "bytes" "context" "crypto/sha256" + b64 "encoding/base64" "encoding/hex" "encoding/json" "fmt" + //"io" "net/http" "strconv" "sync" @@ -44,7 +46,7 @@ func getSignedErr(err error) error { // NewForm returns a new initialized form proxy func NewForm(srv ordering.Service, mngr txn.Manager, p pool.Pool, - ctx serde.Context, fac serde.Factory, pk kyber.Point, blocks blockstore.BlockStore, signer crypto.Signer ) Form { + ctx serde.Context, fac serde.Factory, pk kyber.Point, blocks blockstore.BlockStore, signer crypto.Signer) Form { logger := dela.Logger.With().Timestamp().Str("role", "evoting-proxy").Logger() @@ -57,7 +59,7 @@ func NewForm(srv ordering.Service, mngr txn.Manager, p pool.Pool, pool: p, pk: pk, blocks: blocks, - signer: signer, + signer: signer, } } @@ -75,7 +77,7 @@ type form struct { pool pool.Pool pk kyber.Point blocks blockstore.BlockStore - signer crypto.Signer + signer crypto.Signer } // NewForm implements proxy.Proxy @@ -123,8 +125,7 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { hash.Write(txnID) formID := hash.Sum(nil) - - transactionInfo, err :=h.CreateTransactionInfo(txnID, blockIdx, ptypes.UnknownTransactionStatus) + transactionInfoToSend, err := h.CreateTransactionInfoToSend(txnID, blockIdx, ptypes.UnknownTransactionStatus) if err != nil { http.Error(w, "failed to create transaction info: "+err.Error(), http.StatusInternalServerError) return @@ -133,8 +134,7 @@ func (h *form) NewForm(w http.ResponseWriter, r *http.Request) { // return the formID response := ptypes.CreateFormResponse{ FormID: hex.EncodeToString(formID), - TransactionInfo : transactionInfo, - + Token: transactionInfoToSend.Token, } // sign the response @@ -169,6 +169,8 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { return } + h.logger.Info().Msg(fmt.Sprintf("NewFormVote: %v", req)) + elecMD, err := h.getFormsMetadata() if err != nil { http.Error(w, "failed to get form metadata", http.StatusNotFound) @@ -222,14 +224,14 @@ func (h *form) NewFormVote(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCastVote, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) + h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } @@ -272,6 +274,7 @@ func (h *form) EditForm(w http.ResponseWriter, r *http.Request) { InternalError(w, r, getSignedErr(err), nil) return } + switch req.Action { case "open": h.openForm(formID, w, r) @@ -303,14 +306,14 @@ func (h *form) openForm(formID string, w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdOpenForm, evoting.FormArg, data) + txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdOpenForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) + h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } // closeForm closes a form. @@ -329,14 +332,14 @@ func (h *form) closeForm(formIDHex string, w http.ResponseWriter, r *http.Reques } // create the transaction and add it to the pool - txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCloseForm, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) + h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } @@ -368,14 +371,14 @@ func (h *form) combineShares(formIDHex string, w http.ResponseWriter, r *http.Re } // create the transaction and add it to the pool - txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) + h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } // cancelForm cancels a form. @@ -394,14 +397,14 @@ func (h *form) cancelForm(formIDHex string, w http.ResponseWriter, r *http.Reque } // create the transaction and add it to the pool - txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) + h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } // Form implements proxy.Proxy. The request should not be signed because it @@ -458,7 +461,6 @@ func (h *form) Form(w http.ResponseWriter, r *http.Request) { Voters: form.Suffragia.UserIDs, } - sendResponse(w, response) } @@ -563,47 +565,51 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { } // create the transaction and add it to the pool - txnID,lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) + txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCombineShares, evoting.FormArg, data) if err != nil { http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) return } //send the transaction - h.sendTransactionInfo(w, txnID, lastBlock,ptypes.UnknownTransactionStatus) + h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } -//IsTxnIncluded +// IsTxnIncluded func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) - var content ptypes.TransactionInfo + // check if the formID is valid + if vars == nil || vars["token"] == "" { + http.Error(w, fmt.Sprintf("token not found: %v", vars), http.StatusInternalServerError) + return + } - + token := vars["token"] - err := json.NewDecoder(r.Body).Decode(&content) + marshall, err := b64.URLEncoding.DecodeString(token) if err != nil { - http.Error(w, "failed to decode request: "+err.Error(), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("failed to decode token: %v", err), http.StatusInternalServerError) return } + var content ptypes.TransactionInfo + json.Unmarshal(marshall, &content) + //h.logger.Info().Msg(fmt.Sprintf("Transaction infos: %+v", content)) // get the status of the transaction as byte if content.Status != ptypes.UnknownTransactionStatus { - http.Error(w, "the transaction status is not unknown", http.StatusBadRequest) + http.Error(w, "the transaction status is known", http.StatusBadRequest) return } - - - - - signature,err := h.signer.GetSignatureFactory().SignatureOf(h.context, content.Signature) + // get the signature as a crypto.Signature + signature, err := h.signer.GetSignatureFactory().SignatureOf(h.context, content.Signature) if err != nil { http.Error(w, fmt.Sprintf("failed to get Signature: %v", err), http.StatusInternalServerError) return } - // check if the hash is valid if !h.checkHash(content.Status, content.TransactionID, content.LastBlockIdx, content.Time, content.Hash) { @@ -618,12 +624,12 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { } // check if if was submited not to long ago - if time.Now().Unix() - content.Time > int64(maxTimeTransactionCheck) { + if time.Now().Unix()-content.Time > int64(maxTimeTransactionCheck) { http.Error(w, "the transaction is too old", http.StatusInternalServerError) return } - if time.Now().Unix() - content.Time < 0 { + if time.Now().Unix()-content.Time < 0 { http.Error(w, "the transaction is from the future", http.StatusInternalServerError) return } @@ -631,19 +637,16 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { // check if the transaction is included in the blockchain newStatus, idx := h.checkTxnIncluded(content.TransactionID, content.LastBlockIdx) - - err = h.sendTransactionInfo(w, content.TransactionID, idx,newStatus) + err = h.sendTransactionInfo(w, content.TransactionID, idx, newStatus) if err != nil { http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) return } - -} - +} // checkHash checks if the hash is valid -func (h *form) checkHash(status ptypes.TransactionStatus , transactionID []byte, LastBlockIdx uint64, Time int64, Hash []byte) bool { +func (h *form) checkHash(status ptypes.TransactionStatus, transactionID []byte, LastBlockIdx uint64, Time int64, Hash []byte) bool { // create the hash hash := sha256.New() hash.Write([]byte{byte(status)}) @@ -660,43 +663,40 @@ func (h *form) checkHash(status ptypes.TransactionStatus , transactionID []byte, } // checkSignature checks if the signature is valid -func (h *form) checkSignature(Hash []byte, Signature crypto.Signature ) bool { +func (h *form) checkSignature(Hash []byte, Signature crypto.Signature) bool { // check if the signature is valid return h.signer.GetPublicKey().Verify(Hash, Signature) == nil } // checkTxnIncluded checks if the transaction is included in the blockchain -func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptypes.TransactionStatus,uint64) { +func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptypes.TransactionStatus, uint64) { // first get the block idx := lastBlockIdx - for (true) { + for true { + blockLink, err := h.blocks.GetByIndex(idx) + // if we reached the end of the blockchain + if err != nil { + return ptypes.UnknownTransactionStatus, idx - 1 + } - blockLink, err := h.blocks.GetByIndex(idx) - // if we reached the end of the blockchain - if err != nil { - return ptypes.UnknownTransactionStatus,idx-1 - } + transactions := blockLink.GetBlock().GetTransactions() + for _, txn := range transactions { + if bytes.Equal(txn.GetID(), transactionID) { + return ptypes.IncludedTransaction, blockLink.GetBlock().GetIndex() + } - transactions := blockLink.GetBlock().GetTransactions() - for _, txn := range transactions { - if bytes.Equal(txn.GetID(), transactionID) { - return ptypes.IncludedTransaction ,blockLink.GetBlock().GetIndex() } + idx++ } - idx++ -} - - return ptypes.RejectedTransaction,idx-1 + return ptypes.RejectedTransaction, idx - 1 - } - // waitForTxnID blocks until `ID` is included or `events` is closed. func (h *form) waitForTxnID(events <-chan ordering.Event, ID []byte) error { for event := range events { @@ -775,7 +775,7 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, return form, nil } -// submitTxn submits a transaction +// submitTxn submits a transaction // Returns the transaction ID. func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, cmdArg string, payload []byte) ([]byte, uint64, error) { @@ -802,7 +802,7 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, if err != nil { return nil, 0, xerrors.Errorf("failed to get last block: %v", err) } - lastBlockIdx:=lastBlock.GetBlock().GetIndex() + lastBlockIdx := lastBlock.GetBlock().GetIndex() err = h.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID @@ -842,58 +842,69 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, return false, nil }*/ -func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (error) { +func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) error { - response,err := h.CreateTransactionInfo(txnID, lastBlockIdx, status) + response, err := h.CreateTransactionInfoToSend(txnID, lastBlockIdx, status) if err != nil { return xerrors.Errorf("failed to create transaction info: %v", err) } - return sendResponse(w, response) } -func (h *form) CreateTransactionInfo( txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (ptypes.TransactionInfo,error) { +func (h *form) CreateTransactionInfoToSend(txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (ptypes.TransactionInfoToSend, error) { - time:=time.Now().Unix() - hash:=sha256.New() + time := time.Now().Unix() + hash := sha256.New() // write status which is a byte to the hash as a []byte hash.Write([]byte{byte(status)}) hash.Write(txnID) - hash.Write([]byte(strconv.FormatUint(lastBlockIdx,10))) - hash.Write([]byte(strconv.FormatInt(time,10))) + hash.Write([]byte(strconv.FormatUint(lastBlockIdx, 10))) + hash.Write([]byte(strconv.FormatInt(time, 10))) - finalHash:=hash.Sum(nil) + finalHash := hash.Sum(nil) - signature,err:=h.signer.Sign(finalHash) + signature, err := h.signer.Sign(finalHash) - if err!=nil{ - return ptypes.TransactionInfo{} , xerrors.Errorf("failed to sign transaction info: %v", err) + if err != nil { + return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to sign transaction info: %v", err) } //convert signature to []byte - signatureBin,err :=signature.MarshalBinary() - - if err!=nil{ - return ptypes.TransactionInfo{} , xerrors.Errorf("failed to marshal signature: %v", err) + signatureBin, err := signature.Serialize(h.context) + if err != nil { + return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal signature: %v", err) } - - response := ptypes.TransactionInfo{ - Status: status, + infos := ptypes.TransactionInfo{ + Status: status, TransactionID: txnID, - LastBlockIdx: lastBlockIdx, - Time: time, - Hash: finalHash, - Signature: signatureBin, - + LastBlockIdx: lastBlockIdx, + Time: time, + Hash: finalHash, + Signature: signatureBin, } + marshal, err := json.Marshal(infos) + if err != nil { + return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal transaction info: %v", err) + } + + token := b64.URLEncoding.EncodeToString(marshal) - return response,nil + response := ptypes.TransactionInfoToSend{ + Status: status, + Token: token, + } + h.logger.Info().Msg(fmt.Sprintf("Transaction info: %v", response)) + return response, nil } -func sendResponse(w http.ResponseWriter, response any) (error){ +func sendResponse(w http.ResponseWriter, response any) error { + w.Header().Set("Content-Type", "application/json") + + // Status et token + err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, "failed to write in ResponseWriter: "+err.Error(), @@ -904,7 +915,6 @@ func sendResponse(w http.ResponseWriter, response any) (error){ return nil } - // createTransaction creates a transaction with the given command and payload. func createTransaction(manager txn.Manager, commandType evoting.Command, commandArg string, buf []byte) (txn.Transaction, error) { diff --git a/proxy/mod.go b/proxy/mod.go index 1c02ad38f..44fd09600 100644 --- a/proxy/mod.go +++ b/proxy/mod.go @@ -34,7 +34,7 @@ type Form interface { Form(http.ResponseWriter, *http.Request) // DELETE /forms/{formID} DeleteForm(http.ResponseWriter, *http.Request) - // GET /transactions + // GET /transactions/{token} IsTxnIncluded(http.ResponseWriter, *http.Request) } diff --git a/proxy/types/election.go b/proxy/types/election.go index d27b3be9f..974ba8600 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -29,7 +29,7 @@ type CreateFormRequest struct { // CreateFormResponse defines the HTTP response when creating a form type CreateFormResponse struct { FormID string // hex-encoded - TransactionInfo TransactionInfo + Token string } // CastVoteRequest defines the HTTP request for casting a vote @@ -49,7 +49,7 @@ type EGPairJSON struct { C []byte } -// TransactionInfo defines the HTTP response when sending a transaction to the blockchain +// TransactionInfo defines the information of a transaction type TransactionInfo struct { Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected TransactionID []byte @@ -59,6 +59,13 @@ type TransactionInfo struct { Signature []byte // signature of the transaction } +// TransactionInfoToSend defines the HTTP response when sending transaction infos to the client +type TransactionInfoToSend struct { + Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected + Token string +} + + // UpdateFormRequest defines the HTTP request for updating a form diff --git a/runSystems.sh b/runSystems.sh index 8d64a9730..30c789539 100755 --- a/runSystems.sh +++ b/runSystems.sh @@ -176,7 +176,7 @@ if [ "$BACKEND" == true ]; then if tmux has-session -t $s 2>/dev/null; then # window for the backend tmux new-window -t $s -n "backend" - tmux send-keys -t $s:{end} "cd web/backend && npm start" C-m + tmux send-keys -t $s:{end} "cd web/backend && npm install && cp config.env.template config.env && npm start" C-m else #run it in the current shell cd web/backend && npm start @@ -187,7 +187,8 @@ fi if [ "$FRONTEND" == true ]; then if tmux has-session -t $s 2>/dev/null; then tmux new-window -t $s -n "frontend" - tmux send-keys -t $s:{end} "cd web/frontend && REACT_APP_PROXY=http://localhost:9081 REACT_APP_NOMOCK=on npm start" C-m + tmux send-keys -t $s:{end} "cd web/frontend && npm install && REACT_APP_PROXY=http://localhost:9081 REACT_APP_NOMOCK=on npm start" C-m + else #run it in the current shell cd web/frontend && REACT_APP_PROXY=http://localhost:9081 REACT_APP_NOMOCK=on npm start diff --git a/web/backend/package-lock.json b/web/backend/package-lock.json index b0886e73f..c7ab00a72 100644 --- a/web/backend/package-lock.json +++ b/web/backend/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@dedis/kyber": "^3.4.4", "axios": "^0.24.0", + "casbin": "^5.19.1", "cookie-parser": "^1.4.6", "crypto": "^1.0.1", "express": "^4.17.2", @@ -946,6 +947,11 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/await-lock": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==" + }, "node_modules/axios": { "version": "0.24.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", @@ -1222,6 +1228,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/casbin": { + "version": "5.19.3", + "resolved": "https://registry.npmjs.org/casbin/-/casbin-5.19.3.tgz", + "integrity": "sha512-NV1pnqKCmmzoEASy/V9eiEH23uHMENzGn0N0rzwS91aEQSEg3/Fj4/zZJ/tgDIcLKC13uyXQmSFXAENsF11nQw==", + "dependencies": { + "await-lock": "^2.0.1", + "csv-parse": "^4.15.3", + "expression-eval": "^5.0.0", + "picomatch": "^2.2.3" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1521,6 +1538,11 @@ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" }, + "node_modules/csv-parse": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", + "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2351,6 +2373,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/expression-eval": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expression-eval/-/expression-eval-5.0.0.tgz", + "integrity": "sha512-2H7OBTa/UKBgTVRRb3/lXd+D89cLjClNtldnzOpZYWZK1zBLIlrz8BLWp5f81AAYOc37GbhkCRXtl5Z/q4D91g==", + "dependencies": { + "jsep": "^0.3.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3332,6 +3362,14 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsep": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.3.5.tgz", + "integrity": "sha512-AoRLBDc6JNnKjNcmonituEABS5bcfqDhQAWWXNTFrqu6nVXBpBAGfcoTGZMFlIrh9FjmE1CQyX9CTNwZrXMMDA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -4146,7 +4184,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -5998,6 +6035,11 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "await-lock": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==" + }, "axios": { "version": "0.24.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", @@ -6234,6 +6276,17 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, + "casbin": { + "version": "5.19.3", + "resolved": "https://registry.npmjs.org/casbin/-/casbin-5.19.3.tgz", + "integrity": "sha512-NV1pnqKCmmzoEASy/V9eiEH23uHMENzGn0N0rzwS91aEQSEg3/Fj4/zZJ/tgDIcLKC13uyXQmSFXAENsF11nQw==", + "requires": { + "await-lock": "^2.0.1", + "csv-parse": "^4.15.3", + "expression-eval": "^5.0.0", + "picomatch": "^2.2.3" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -6480,6 +6533,11 @@ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" }, + "csv-parse": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", + "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7133,6 +7191,14 @@ } } }, + "expression-eval": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expression-eval/-/expression-eval-5.0.0.tgz", + "integrity": "sha512-2H7OBTa/UKBgTVRRb3/lXd+D89cLjClNtldnzOpZYWZK1zBLIlrz8BLWp5f81AAYOc37GbhkCRXtl5Z/q4D91g==", + "requires": { + "jsep": "^0.3.0" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7848,6 +7914,11 @@ "argparse": "^2.0.1" } }, + "jsep": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.3.5.tgz", + "integrity": "sha512-AoRLBDc6JNnKjNcmonituEABS5bcfqDhQAWWXNTFrqu6nVXBpBAGfcoTGZMFlIrh9FjmE1CQyX9CTNwZrXMMDA==" + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -8486,8 +8557,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "prelude-ls": { "version": "1.2.1", diff --git a/web/backend/yarn.lock b/web/backend/yarn.lock index 8e581a821..1917281fc 100644 --- a/web/backend/yarn.lock +++ b/web/backend/yarn.lock @@ -3,286 +3,236 @@ "@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + "integrity" "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==" + "resolved" "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + "version" "0.8.1" dependencies: "@jridgewell/trace-mapping" "0.3.9" "@dedis/kyber@^3.4.4": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@dedis/kyber/-/kyber-3.4.4.tgz#2a8fec9c59078a4c3f11f6876c9cf2b203a01509" - integrity sha512-RarB9jDqni8sb1v+ah1pdC7jC0gsLGcqDTwO3TVf4PWsPksVR1iQtKbfoPf6bhOXiN5T91l+Eq1gadh1ZFSU1Q== + "integrity" "sha512-RarB9jDqni8sb1v+ah1pdC7jC0gsLGcqDTwO3TVf4PWsPksVR1iQtKbfoPf6bhOXiN5T91l+Eq1gadh1ZFSU1Q==" + "resolved" "https://registry.npmjs.org/@dedis/kyber/-/kyber-3.4.4.tgz" + "version" "3.4.4" dependencies: "@stablelib/blake2xs" "^0.10.4" "@types/bn.js" "^4.11.6" "@types/elliptic" "^6.4.12" - bn.js "^5.1.2" - crypto-browserify "^3.12.0" - elliptic "^6.5.3" - hash.js "^1.1.3" - -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.4.0" - globals "^13.15.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@humanwhocodes/config-array@^0.11.6": - version "0.11.6" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.6.tgz#6a51d603a3aaf8d4cf45b42b3f2ac9318a4adc4b" - integrity sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg== + "bn.js" "^5.1.2" + "crypto-browserify" "^3.12.0" + "elliptic" "^6.5.3" + "hash.js" "^1.1.3" + +"@eslint/eslintrc@^1.3.0": + "integrity" "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==" + "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz" + "version" "1.3.0" + dependencies: + "ajv" "^6.12.4" + "debug" "^4.3.2" + "espree" "^9.3.2" + "globals" "^13.15.0" + "ignore" "^5.2.0" + "import-fresh" "^3.2.1" + "js-yaml" "^4.1.0" + "minimatch" "^3.1.2" + "strip-json-comments" "^3.1.1" + +"@humanwhocodes/config-array@^0.9.2": + "integrity" "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" + "version" "0.9.5" dependencies: "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + "debug" "^4.1.1" + "minimatch" "^3.0.4" "@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + "integrity" "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + "version" "1.2.1" "@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + "integrity" "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==" + "resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz" + "version" "3.0.7" "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + "integrity" "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" + "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz" + "version" "1.4.13" "@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + "integrity" "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==" + "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + "version" "0.3.9" dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@lmdb/lmdb-darwin-arm64@2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.6.8.tgz#f909f3745f30e9215ea4e8bd62ad71e4f50edfb3" - integrity sha512-u3BSAWCiyN8DZnXx4HdQjFTD2mlAnRHCmtgjQY7RwH99bFgZQzGpqvtM4TNh5bLuInxovyilI3qNmqUBEqbgtg== - -"@lmdb/lmdb-darwin-x64@2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.6.8.tgz#fa8b47359b761f3d4350484ee8703335d7820e3c" - integrity sha512-btsrk8V8rjYxMgQX0hduqmhf0BFHTcjF4ZO7b796rMonGwhIZYdKE8CY1l/AQfRhoB6Pi1PKwhyO6B8W9zxNCQ== - -"@lmdb/lmdb-linux-arm64@2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.6.8.tgz#12b51bb057d4ae8b6705198d2b5b2e6ec2d7c3d5" - integrity sha512-TVjR7QSSbuJqhCib/nqpRPvCA5rShm5ep6VJ/O0Wz04z1zIuRjkEYFwx9bOlRqmyu33gvab3MWqDyA33BN94sw== - -"@lmdb/lmdb-linux-arm@2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.6.8.tgz#1165d698e526d6135c3baf6993f2b8e8da0aed6f" - integrity sha512-J4LlzHWkdWLNFlJJ2ZZlQF6k2Dejjd0e5kBMclvK4BdEJ5OOqrOZIAbDUb/efOhbmlR4AGShzIPviCRqOufU1w== - -"@lmdb/lmdb-linux-x64@2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.6.8.tgz#5f9b8fcf80de115fc9a1610110e5e14751649316" - integrity sha512-VqeBBNiQ5bkvIHW3gATypT6OMBCdHJtZqSvYXmQL5p2hJ/VqV1H3W2HBrAhKTs0ap4aUBPmuHTvoC87lgSCpmg== - -"@lmdb/lmdb-win32-x64@2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.6.8.tgz#7ddd4cd1202981efec7bb285f9bab50040f631e7" - integrity sha512-duVM8zq1FZWzu9tzwhOMAkbCiz7EZkXqPGv3EtG+FLPgBNiEih1AXB7WsrYufJZgFUzKOJQQyNVVA2/UJDOtjQ== - -"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.1.2.tgz#9571b87be3a3f2c46de05585470bc4f3af2f6f00" - integrity sha512-TyVLn3S/+ikMDsh0gbKv2YydKClN8HaJDDpONlaZR+LVJmsxLFUgA+O7zu59h9+f9gX1aj/ahw9wqa6rosmrYQ== - -"@msgpackr-extract/msgpackr-extract-darwin-x64@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.1.2.tgz#bfbc6936ede2955218f5621a675679a5fe8e6f4c" - integrity sha512-YPXtcVkhmVNoMGlqp81ZHW4dMxK09msWgnxtsDpSiZwTzUBG2N+No2bsr7WMtBKCVJMSD6mbAl7YhKUqkp/Few== - -"@msgpackr-extract/msgpackr-extract-linux-arm64@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.1.2.tgz#22555e28382af2922e7450634c8a2f240bb9eb82" - integrity sha512-vHZ2JiOWF2+DN9lzltGbhtQNzDo8fKFGrf37UJrgqxU0yvtERrzUugnfnX1wmVfFhSsF8OxrfqiNOUc5hko1Zg== - -"@msgpackr-extract/msgpackr-extract-linux-arm@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.1.2.tgz#ffb6ae1beea7ac572b6be6bf2a8e8162ebdd8be7" - integrity sha512-42R4MAFeIeNn+L98qwxAt360bwzX2Kf0ZQkBBucJ2Ircza3asoY4CDbgiu9VWklq8gWJVSJSJBwDI+c/THiWkA== - -"@msgpackr-extract/msgpackr-extract-linux-x64@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.1.2.tgz#7caf62eebbfb1345de40f75e89666b3d4194755f" - integrity sha512-RjRoRxg7Q3kPAdUSC5EUUPlwfMkIVhmaRTIe+cqHbKrGZ4M6TyCA/b5qMaukQ/1CHWrqYY2FbKOAU8Hg0pQFzg== - -"@msgpackr-extract/msgpackr-extract-win32-x64@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.1.2.tgz#f2d8b9ddd8d191205ed26ce54aba3dfc5ae3e7c9" - integrity sha512-rIZVR48zA8hGkHIK7ED6+ZiXsjRCcAVBJbm8o89OKAMTmEAQ2QvoOxoiu3w2isAaWwzgtQIOFIqHwvZDyLKCvw== +"@lmdb/lmdb-darwin-arm64@2.4.5": + "integrity" "sha512-JlYSjhyPUQI2dyH497WkZ+AqPQZZ0v3xyAmjMpKrucgnoPGoNbSYg/LNvfVx9WE2iQunZ7vUkofunFZxuQLDcQ==" + "resolved" "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.4.5.tgz" + "version" "2.4.5" + +"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.0.2": + "integrity" "sha512-FMX5i7a+ojIguHpWbzh5MCsCouJkwf4z4ejdUY/fsgB9Vkdak4ZnoIEskOyOUMMB4lctiZFGszFQJXUeFL8tRg==" + "resolved" "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.0.2.tgz" + "version" "2.0.2" "@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + "version" "2.1.5" dependencies: "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" + "run-parallel" "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + "version" "2.0.5" -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== +"@nodelib/fs.walk@^1.2.3": + "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + "version" "1.2.8" dependencies: "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" + "fastq" "^1.6.0" + +"@sindresorhus/is@^0.14.0": + "integrity" "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + "resolved" "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" + "version" "0.14.0" "@stablelib/binary@^0.7.2": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-0.7.2.tgz#1b3392170c8a8741c8b8f843ea294de71aeb2cf7" - integrity sha512-J7iGppeKR112ICTZTAoALcT3yBpTrd2Z/F0wwiOUZPVPTDFTQFWHZZdYzfal9+mY1uMUPRSEnNmDuXRZbtE8Xg== + "integrity" "sha512-J7iGppeKR112ICTZTAoALcT3yBpTrd2Z/F0wwiOUZPVPTDFTQFWHZZdYzfal9+mY1uMUPRSEnNmDuXRZbtE8Xg==" + "resolved" "https://registry.npmjs.org/@stablelib/binary/-/binary-0.7.2.tgz" + "version" "0.7.2" dependencies: "@stablelib/int" "^0.5.0" "@stablelib/blake2s@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@stablelib/blake2s/-/blake2s-0.10.4.tgz#8a708f28a9c78d4a1a9fbcc6ce8bacbda469f302" - integrity sha512-IasdklC7YfXXLmVbnsxqmd66+Ki+Ysbp0BtcrNxAtrGx/HRGjkUZbSTbEa7HxFhBWIstJRcE5ExgY+RCqAiULQ== + "integrity" "sha512-IasdklC7YfXXLmVbnsxqmd66+Ki+Ysbp0BtcrNxAtrGx/HRGjkUZbSTbEa7HxFhBWIstJRcE5ExgY+RCqAiULQ==" + "resolved" "https://registry.npmjs.org/@stablelib/blake2s/-/blake2s-0.10.4.tgz" + "version" "0.10.4" dependencies: "@stablelib/binary" "^0.7.2" "@stablelib/hash" "^0.5.0" "@stablelib/wipe" "^0.5.0" "@stablelib/blake2xs@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@stablelib/blake2xs/-/blake2xs-0.10.4.tgz#b3ae9e145cbf924a7f598412b586e4af24d10cb7" - integrity sha512-1N0S4cruso/StV9TmoujPGj3RU0Cy42wlZneBWLWby7m2ssnY57l/CsYQSm03TshOoYss4hqc5kwSy5pmWAdUA== + "integrity" "sha512-1N0S4cruso/StV9TmoujPGj3RU0Cy42wlZneBWLWby7m2ssnY57l/CsYQSm03TshOoYss4hqc5kwSy5pmWAdUA==" + "resolved" "https://registry.npmjs.org/@stablelib/blake2xs/-/blake2xs-0.10.4.tgz" + "version" "0.10.4" dependencies: "@stablelib/blake2s" "^0.10.4" "@stablelib/hash" "^0.5.0" "@stablelib/wipe" "^0.5.0" "@stablelib/hash@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-0.5.0.tgz#89fe9040a3d4383b1921c7d8a60948bc30846068" - integrity sha512-rlNEBTskjKVl9f4rpRgM2GV3IrZWfNJFY5Y/2tmQtA2ozEkPLoUp9J/uJnBRnOpCsuflPW2z+pwqPbEYOPCHwQ== + "integrity" "sha512-rlNEBTskjKVl9f4rpRgM2GV3IrZWfNJFY5Y/2tmQtA2ozEkPLoUp9J/uJnBRnOpCsuflPW2z+pwqPbEYOPCHwQ==" + "resolved" "https://registry.npmjs.org/@stablelib/hash/-/hash-0.5.0.tgz" + "version" "0.5.0" "@stablelib/int@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-0.5.0.tgz#cca9225951d55d2de48656755784788633660c2b" - integrity sha512-cuaPoxm3K14LiEICiA3iz0aeGurg75v+haZMV+xloVTw3CT25oMRJgQ6VxZ2p2cHy4kjhVI68kX4oaYrhnTm+g== + "integrity" "sha512-cuaPoxm3K14LiEICiA3iz0aeGurg75v+haZMV+xloVTw3CT25oMRJgQ6VxZ2p2cHy4kjhVI68kX4oaYrhnTm+g==" + "resolved" "https://registry.npmjs.org/@stablelib/int/-/int-0.5.0.tgz" + "version" "0.5.0" "@stablelib/wipe@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-0.5.0.tgz#a682d5f9448e950e099e537e6f72fc960275d151" - integrity sha512-SifvRV0rTTFR1qEF6G1hondGZyrmiM1laR8PPrO6TZwQG03hJduVbUX8uQk+Q6FdkND2Z9B8uLPyUAquQIk3iA== + "integrity" "sha512-SifvRV0rTTFR1qEF6G1hondGZyrmiM1laR8PPrO6TZwQG03hJduVbUX8uQk+Q6FdkND2Z9B8uLPyUAquQIk3iA==" + "resolved" "https://registry.npmjs.org/@stablelib/wipe/-/wipe-0.5.0.tgz" + "version" "0.5.0" + +"@szmarczak/http-timer@^1.1.2": + "integrity" "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==" + "resolved" "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" + "version" "1.1.2" + dependencies: + "defer-to-connect" "^1.0.1" "@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + "integrity" "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + "resolved" "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" + "version" "1.0.8" "@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + "integrity" "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + "resolved" "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz" + "version" "1.0.9" "@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + "integrity" "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + "resolved" "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz" + "version" "1.0.1" "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "integrity" "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + "resolved" "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz" + "version" "1.0.2" -"@types/bn.js@*": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" - integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== - dependencies: - "@types/node" "*" - -"@types/bn.js@^4.11.6": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== +"@types/bn.js@*", "@types/bn.js@^4.11.6": + "integrity" "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==" + "resolved" "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz" + "version" "4.11.6" dependencies: "@types/node" "*" "@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + "integrity" "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==" + "resolved" "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" dependencies: "@types/connect" "*" "@types/node" "*" "@types/caseless@*": - version "0.12.2" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" - integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== + "integrity" "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" + "resolved" "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz" + "version" "0.12.2" "@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" + "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + "version" "3.4.35" dependencies: "@types/node" "*" "@types/cookie-parser@^1.4.2": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@types/cookie-parser/-/cookie-parser-1.4.3.tgz#3a01df117c5705cf89a84c876b50c5a1fd427a21" - integrity sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w== + "integrity" "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==" + "resolved" "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz" + "version" "1.4.3" dependencies: "@types/express" "*" "@types/elliptic@^6.4.12": - version "6.4.14" - resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.14.tgz#7bbaad60567a588c1f08b10893453e6b9b4de48e" - integrity sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ== + "integrity" "sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==" + "resolved" "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.14.tgz" + "version" "6.4.14" dependencies: "@types/bn.js" "*" "@types/express-serve-static-core@^4.17.18": - version "4.17.31" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" - integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== + "integrity" "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==" + "resolved" "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz" + "version" "4.17.28" dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express-session@^1.17.4": - version "1.17.5" - resolved "https://registry.yarnpkg.com/@types/express-session/-/express-session-1.17.5.tgz#13f48852b4aa60ff595835faeb4b4dda0ba0866e" - integrity sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w== + "integrity" "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==" + "resolved" "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz" + "version" "1.17.4" dependencies: "@types/express" "*" "@types/express@*", "@types/express@^4.17.13": - version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" - integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== + "integrity" "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==" + "resolved" "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz" + "version" "4.17.13" dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.18" @@ -290,2537 +240,3020 @@ "@types/serve-static" "*" "@types/jest@^27.5.1": - version "27.5.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" - integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== + "integrity" "sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ==" + "resolved" "https://registry.npmjs.org/@types/jest/-/jest-27.5.1.tgz" + "version" "27.5.1" dependencies: - jest-matcher-utils "^27.0.0" - pretty-format "^27.0.0" + "jest-matcher-utils" "^27.0.0" + "pretty-format" "^27.0.0" "@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + "integrity" "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + "version" "7.0.11" "@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "integrity" "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + "resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + "version" "0.0.29" -"@types/mime@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/mime@^1": + "integrity" "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "resolved" "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" + "version" "1.3.2" "@types/morgan@^1.9.3": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.3.tgz#ae04180dff02c437312bc0cfb1e2960086b2f540" - integrity sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q== + "integrity" "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==" + "resolved" "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz" + "version" "1.9.3" dependencies: "@types/node" "*" -"@types/node@*": - version "18.11.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.5.tgz#1bc94cf2f9ab5fe33353bc7c79c797dcc5325bef" - integrity sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ== - -"@types/node@^17.0.36": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" - integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@*", "@types/node@^17.0.36": + "integrity" "sha512-V3orv+ggDsWVHP99K3JlwtH20R7J4IhI1Kksgc+64q5VxgfRkQG8Ws3MFm/FZOKDYGy9feGFlZ70/HpCNe9QaA==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.36.tgz" + "version" "17.0.36" "@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + "integrity" "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "resolved" "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" "@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "integrity" "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + "version" "1.2.4" "@types/request@^2.48.8": - version "2.48.8" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.8.tgz#0b90fde3b655ab50976cb8c5ac00faca22f5a82c" - integrity sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ== + "integrity" "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==" + "resolved" "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz" + "version" "2.48.8" dependencies: "@types/caseless" "*" "@types/node" "*" "@types/tough-cookie" "*" - form-data "^2.5.0" - -"@types/semver@^7.3.12": - version "7.3.12" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.12.tgz#920447fdd78d76b19de0438b7f60df3c4a80bf1c" - integrity sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A== + "form-data" "^2.5.0" "@types/serve-static@*": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" - integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + "integrity" "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==" + "resolved" "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz" + "version" "1.13.10" dependencies: - "@types/mime" "*" + "@types/mime" "^1" "@types/node" "*" "@types/tough-cookie@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" - integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== - -"@typescript-eslint/scope-manager@5.41.0": - version "5.41.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz#28e3a41d626288d0628be14cf9de8d49fc30fadf" - integrity sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ== - dependencies: - "@typescript-eslint/types" "5.41.0" - "@typescript-eslint/visitor-keys" "5.41.0" - -"@typescript-eslint/types@5.41.0": - version "5.41.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.41.0.tgz#6800abebc4e6abaf24cdf220fb4ce28f4ab09a85" - integrity sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA== - -"@typescript-eslint/typescript-estree@5.41.0": - version "5.41.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz#bf5c6b3138adbdc73ba4871d060ae12c59366c61" - integrity sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg== - dependencies: - "@typescript-eslint/types" "5.41.0" - "@typescript-eslint/visitor-keys" "5.41.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/utils@^5.10.0": - version "5.41.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.41.0.tgz#f41ae5883994a249d00b2ce69f4188f3a23fa0f9" - integrity sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ== + "integrity" "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==" + "resolved" "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz" + "version" "4.0.2" + +"@typescript-eslint/eslint-plugin@^5.0.0": + "integrity" "sha512-DDrIA7GXtmHXr1VCcx9HivA39eprYBIFxbQEHI6NyraRDxCGpxAFiYQAT/1Y0vh1C+o2vfBiy4IuPoXxtTZCAQ==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.0.tgz" + "version" "5.27.0" + dependencies: + "@typescript-eslint/scope-manager" "5.27.0" + "@typescript-eslint/type-utils" "5.27.0" + "@typescript-eslint/utils" "5.27.0" + "debug" "^4.3.4" + "functional-red-black-tree" "^1.0.1" + "ignore" "^5.2.0" + "regexpp" "^3.2.0" + "semver" "^7.3.7" + "tsutils" "^3.21.0" + +"@typescript-eslint/parser@^5.0.0": + "integrity" "sha512-8oGjQF46c52l7fMiPPvX4It3u3V3JipssqDfHQ2hcR0AeR8Zge+OYyKUCm5b70X72N1qXt0qgHenwN6Gc2SXZA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.27.0.tgz" + "version" "5.27.0" + dependencies: + "@typescript-eslint/scope-manager" "5.27.0" + "@typescript-eslint/types" "5.27.0" + "@typescript-eslint/typescript-estree" "5.27.0" + "debug" "^4.3.4" + +"@typescript-eslint/scope-manager@5.27.0": + "integrity" "sha512-VnykheBQ/sHd1Vt0LJ1JLrMH1GzHO+SzX6VTXuStISIsvRiurue/eRkTqSrG0CexHQgKG8shyJfR4o5VYioB9g==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.27.0.tgz" + "version" "5.27.0" + dependencies: + "@typescript-eslint/types" "5.27.0" + "@typescript-eslint/visitor-keys" "5.27.0" + +"@typescript-eslint/type-utils@5.27.0": + "integrity" "sha512-vpTvRRchaf628Hb/Xzfek+85o//zEUotr1SmexKvTfs7czXfYjXVT/a5yDbpzLBX1rhbqxjDdr1Gyo0x1Fc64g==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.27.0.tgz" + "version" "5.27.0" + dependencies: + "@typescript-eslint/utils" "5.27.0" + "debug" "^4.3.4" + "tsutils" "^3.21.0" + +"@typescript-eslint/types@5.27.0": + "integrity" "sha512-lY6C7oGm9a/GWhmUDOs3xAVRz4ty/XKlQ2fOLr8GAIryGn0+UBOoJDWyHer3UgrHkenorwvBnphhP+zPmzmw0A==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.27.0.tgz" + "version" "5.27.0" + +"@typescript-eslint/typescript-estree@5.27.0": + "integrity" "sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.0.tgz" + "version" "5.27.0" + dependencies: + "@typescript-eslint/types" "5.27.0" + "@typescript-eslint/visitor-keys" "5.27.0" + "debug" "^4.3.4" + "globby" "^11.1.0" + "is-glob" "^4.0.3" + "semver" "^7.3.7" + "tsutils" "^3.21.0" + +"@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@5.27.0": + "integrity" "sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.27.0.tgz" + "version" "5.27.0" dependencies: "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.41.0" - "@typescript-eslint/types" "5.41.0" - "@typescript-eslint/typescript-estree" "5.41.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - semver "^7.3.7" - -"@typescript-eslint/visitor-keys@5.41.0": - version "5.41.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz#d3510712bc07d5540160ed3c0f8f213b73e3bcd9" - integrity sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw== - dependencies: - "@typescript-eslint/types" "5.41.0" - eslint-visitor-keys "^3.3.0" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^8.4.1, acorn@^8.8.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" - integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== - -ajv@^6.10.0, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -array-includes@^3.1.4: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -await-lock@^2.0.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/await-lock/-/await-lock-2.2.2.tgz#a95a9b269bfd2f69d22b17a321686f551152bcef" - integrity sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw== - -axios@^0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== - dependencies: - follow-redirects "^1.14.4" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -casbin@^5.19.1: - version "5.19.1" - resolved "https://registry.yarnpkg.com/casbin/-/casbin-5.19.1.tgz#cf9881b0a52c5b7fd5cf83bb2fabc7c86bc7c022" - integrity sha512-5OgN1+ZPM/AWv09PHrUHROu+fwOlHg+/CneVjej9OMAWqg24M9IVjxR8HkY3dUJOkqEZTpZ1HRLA/eC9mKytvA== - dependencies: - await-lock "^2.0.1" - csv-parse "^4.15.3" - expression-eval "^4.0.0" - picomatch "^2.2.3" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chokidar@^3.5.2: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" + "@typescript-eslint/scope-manager" "5.27.0" + "@typescript-eslint/types" "5.27.0" + "@typescript-eslint/typescript-estree" "5.27.0" + "eslint-scope" "^5.1.1" + "eslint-utils" "^3.0.0" + +"@typescript-eslint/visitor-keys@5.27.0": + "integrity" "sha512-46cYrteA2MrIAjv9ai44OQDUoCZyHeGIc4lsjCUX2WT6r4C+kidz1bNiR4017wHOPUythYeH+Sc7/cFP97KEAA==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.0.tgz" + "version" "5.27.0" + dependencies: + "@typescript-eslint/types" "5.27.0" + "eslint-visitor-keys" "^3.3.0" + +"abbrev@1": + "integrity" "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "resolved" "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + "version" "1.1.1" + +"accepts@~1.3.8": + "integrity" "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==" + "resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + "version" "1.3.8" + dependencies: + "mime-types" "~2.1.34" + "negotiator" "0.6.3" + +"acorn-jsx@^5.3.2": + "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + "version" "5.3.2" + +"acorn-walk@^8.1.1": + "integrity" "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + "resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + "version" "8.2.0" + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^8.4.1", "acorn@^8.7.1": + "integrity" "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" + "version" "8.7.1" + +"ajv@^6.10.0", "ajv@^6.12.4": + "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + "version" "6.12.6" + dependencies: + "fast-deep-equal" "^3.1.1" + "fast-json-stable-stringify" "^2.0.0" + "json-schema-traverse" "^0.4.1" + "uri-js" "^4.2.2" + +"ansi-align@^3.0.0": + "integrity" "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==" + "resolved" "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "string-width" "^4.1.0" + +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" + +"ansi-styles@^4.0.0", "ansi-styles@^4.1.0": + "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "color-convert" "^2.0.1" + +"ansi-styles@^5.0.0": + "integrity" "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + "version" "5.2.0" + +"anymatch@~3.1.2": + "integrity" "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" + "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "normalize-path" "^3.0.0" + "picomatch" "^2.0.4" + +"arg@^4.1.0": + "integrity" "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + "resolved" "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + "version" "4.1.3" + +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" + +"array-flatten@1.1.1": + "integrity" "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + "version" "1.1.1" + +"array-includes@^3.1.4": + "integrity" "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==" + "resolved" "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz" + "version" "3.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" + "get-intrinsic" "^1.1.1" + "is-string" "^1.0.7" + +"array-union@^2.1.0": + "integrity" "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + "version" "2.1.0" + +"array.prototype.flat@^1.2.5": + "integrity" "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==" + "resolved" "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz" + "version" "1.3.0" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.2" + "es-shim-unscopables" "^1.0.0" + +"asn1.js@^5.2.0": + "integrity" "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==" + "resolved" "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz" + "version" "5.4.1" + dependencies: + "bn.js" "^4.0.0" + "inherits" "^2.0.1" + "minimalistic-assert" "^1.0.0" + "safer-buffer" "^2.1.0" + +"asynckit@^0.4.0": + "integrity" "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "resolved" "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "version" "0.4.0" + +"await-lock@^2.0.1": + "integrity" "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==" + "resolved" "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz" + "version" "2.2.2" + +"axios@^0.24.0": + "integrity" "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==" + "resolved" "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz" + "version" "0.24.0" + dependencies: + "follow-redirects" "^1.14.4" + +"balanced-match@^1.0.0": + "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + "version" "1.0.2" + +"basic-auth@~2.0.1": + "integrity" "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==" + "resolved" "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "safe-buffer" "5.1.2" + +"binary-extensions@^2.0.0": + "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + "version" "2.2.0" + +"bn.js@^4.0.0": + "integrity" "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + "version" "4.12.0" + +"bn.js@^4.1.0": + "integrity" "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + "version" "4.12.0" + +"bn.js@^4.11.9": + "integrity" "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + "version" "4.12.0" + +"bn.js@^5.0.0", "bn.js@^5.1.1", "bn.js@^5.1.2": + "integrity" "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" + "version" "5.2.1" + +"body-parser@1.20.0": + "integrity" "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==" + "resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz" + "version" "1.20.0" + dependencies: + "bytes" "3.1.2" + "content-type" "~1.0.4" + "debug" "2.6.9" + "depd" "2.0.0" + "destroy" "1.2.0" + "http-errors" "2.0.0" + "iconv-lite" "0.4.24" + "on-finished" "2.4.1" + "qs" "6.10.3" + "raw-body" "2.5.1" + "type-is" "~1.6.18" + "unpipe" "1.0.0" + +"boxen@^5.0.0": + "integrity" "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==" + "resolved" "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "ansi-align" "^3.0.0" + "camelcase" "^6.2.0" + "chalk" "^4.1.0" + "cli-boxes" "^2.2.1" + "string-width" "^4.2.2" + "type-fest" "^0.20.2" + "widest-line" "^3.1.0" + "wrap-ansi" "^7.0.0" + +"brace-expansion@^1.1.7": + "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + "version" "1.1.11" + dependencies: + "balanced-match" "^1.0.0" + "concat-map" "0.0.1" + +"braces@^3.0.2", "braces@~3.0.2": + "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "fill-range" "^7.0.1" + +"brorand@^1.0.1", "brorand@^1.1.0": + "integrity" "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + "resolved" "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" + "version" "1.1.0" + +"browserify-aes@^1.0.0", "browserify-aes@^1.0.4": + "integrity" "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==" + "resolved" "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "buffer-xor" "^1.0.3" + "cipher-base" "^1.0.0" + "create-hash" "^1.1.0" + "evp_bytestokey" "^1.0.3" + "inherits" "^2.0.1" + "safe-buffer" "^5.0.1" + +"browserify-cipher@^1.0.0": + "integrity" "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==" + "resolved" "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "browserify-aes" "^1.0.4" + "browserify-des" "^1.0.0" + "evp_bytestokey" "^1.0.0" + +"browserify-des@^1.0.0": + "integrity" "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==" + "resolved" "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "cipher-base" "^1.0.1" + "des.js" "^1.0.0" + "inherits" "^2.0.1" + "safe-buffer" "^5.1.2" + +"browserify-rsa@^4.0.0", "browserify-rsa@^4.0.1": + "integrity" "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==" + "resolved" "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "bn.js" "^5.0.0" + "randombytes" "^2.0.1" + +"browserify-sign@^4.0.0": + "integrity" "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==" + "resolved" "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz" + "version" "4.2.1" + dependencies: + "bn.js" "^5.1.1" + "browserify-rsa" "^4.0.1" + "create-hash" "^1.2.0" + "create-hmac" "^1.1.7" + "elliptic" "^6.5.3" + "inherits" "^2.0.4" + "parse-asn1" "^5.1.5" + "readable-stream" "^3.6.0" + "safe-buffer" "^5.2.0" + +"buffer-xor@^1.0.3": + "integrity" "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + "resolved" "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" + "version" "1.0.3" + +"bytes@3.1.2": + "integrity" "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + "version" "3.1.2" + +"cacheable-request@^6.0.0": + "integrity" "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==" + "resolved" "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" + "version" "6.1.0" + dependencies: + "clone-response" "^1.0.2" + "get-stream" "^5.1.0" + "http-cache-semantics" "^4.0.0" + "keyv" "^3.0.0" + "lowercase-keys" "^2.0.0" + "normalize-url" "^4.1.0" + "responselike" "^1.0.2" + +"call-bind@^1.0.0", "call-bind@^1.0.2": + "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==" + "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "function-bind" "^1.1.1" + "get-intrinsic" "^1.0.2" + +"callsites@^3.0.0": + "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + "version" "3.1.0" + +"camelcase@^6.2.0": + "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + "version" "6.3.0" + +"casbin@^5.19.1": + "integrity" "sha512-NV1pnqKCmmzoEASy/V9eiEH23uHMENzGn0N0rzwS91aEQSEg3/Fj4/zZJ/tgDIcLKC13uyXQmSFXAENsF11nQw==" + "resolved" "https://registry.npmjs.org/casbin/-/casbin-5.19.3.tgz" + "version" "5.19.3" + dependencies: + "await-lock" "^2.0.1" + "csv-parse" "^4.15.3" + "expression-eval" "^5.0.0" + "picomatch" "^2.2.3" + +"chalk@^4.0.0", "chalk@^4.1.0": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" + +"chokidar@^3.5.2": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" + dependencies: + "anymatch" "~3.1.2" + "braces" "~3.0.2" + "glob-parent" "~5.1.2" + "is-binary-path" "~2.1.0" + "is-glob" "~4.0.1" + "normalize-path" "~3.0.0" + "readdirp" "~3.6.0" optionalDependencies: - fsevents "~2.3.2" - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -combined-stream@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -confusing-browser-globals@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" - integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookie-parser@^1.4.6: - version "1.4.6" - resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.6.tgz#3ac3a7d35a7a03bbc7e365073a26074824214594" - integrity sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA== - dependencies: - cookie "0.4.1" - cookie-signature "1.0.6" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== - -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-browserify@^3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -crypto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" - integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== - -cssfilter@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" - integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== - -csv-parse@^4.15.3: - version "4.16.3" - resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" - integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== - -debug@2.6.9, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.1, debug@^4.3.0, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -depd@2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dotenv@^16.0.1: - version "16.0.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" - integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.2" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== - dependencies: - has "^1.0.3" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-config-airbnb-base@^15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" - integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== - dependencies: - confusing-browser-globals "^1.0.10" - object.assign "^4.1.2" - object.entries "^1.1.5" - semver "^6.3.0" - -eslint-config-airbnb-typescript@^16.1.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-16.2.0.tgz#9193fafd62f1cbf444895f4495eae334baf3265b" - integrity sha512-OUaMPZpTOZGKd5tXOjJ9PRU4iYNW/Z5DoHIynjsVK/FpkWdiY5+nxQW6TiJAlLwVI1l53xUOrnlZWtVBVQzuWA== - dependencies: - eslint-config-airbnb-base "^15.0.0" - -eslint-config-prettier@^8.3.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== - -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== - dependencies: - debug "^3.2.7" - resolve "^1.20.0" - -eslint-module-utils@^2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.25.4: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== - dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" - has "^1.0.3" - is-core-module "^2.8.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" - -eslint-plugin-jest@^26.1.0: - version "26.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.9.0.tgz#7931c31000b1c19e57dbfb71bbf71b817d1bf949" - integrity sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng== + "fsevents" "~2.3.2" + +"ci-info@^2.0.0": + "integrity" "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + "version" "2.0.0" + +"cipher-base@^1.0.0", "cipher-base@^1.0.1", "cipher-base@^1.0.3": + "integrity" "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==" + "resolved" "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "inherits" "^2.0.1" + "safe-buffer" "^5.0.1" + +"cli-boxes@^2.2.1": + "integrity" "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" + "resolved" "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" + "version" "2.2.1" + +"clone-response@^1.0.2": + "integrity" "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==" + "resolved" "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "mimic-response" "^1.0.0" + +"color-convert@^2.0.1": + "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "color-name" "~1.1.4" + +"color-name@~1.1.4": + "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + "version" "1.1.4" + +"combined-stream@^1.0.6": + "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==" + "resolved" "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + "version" "1.0.8" + dependencies: + "delayed-stream" "~1.0.0" + +"commander@^2.20.3": + "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + "version" "2.20.3" + +"concat-map@0.0.1": + "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "version" "0.0.1" + +"configstore@^5.0.1": + "integrity" "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==" + "resolved" "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "dot-prop" "^5.2.0" + "graceful-fs" "^4.1.2" + "make-dir" "^3.0.0" + "unique-string" "^2.0.0" + "write-file-atomic" "^3.0.0" + "xdg-basedir" "^4.0.0" + +"confusing-browser-globals@^1.0.10": + "integrity" "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + "resolved" "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" + "version" "1.0.11" + +"content-disposition@0.5.4": + "integrity" "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + "version" "0.5.4" + dependencies: + "safe-buffer" "5.2.1" + +"content-type@~1.0.4": + "integrity" "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "resolved" "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + "version" "1.0.4" + +"cookie-parser@^1.4.6": + "integrity" "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==" + "resolved" "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz" + "version" "1.4.6" + dependencies: + "cookie" "0.4.1" + "cookie-signature" "1.0.6" + +"cookie-signature@1.0.6": + "integrity" "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "resolved" "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + "version" "1.0.6" + +"cookie@0.4.1": + "integrity" "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz" + "version" "0.4.1" + +"cookie@0.4.2": + "integrity" "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + "version" "0.4.2" + +"cookie@0.5.0": + "integrity" "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" + "version" "0.5.0" + +"create-ecdh@^4.0.0": + "integrity" "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==" + "resolved" "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" + "version" "4.0.4" + dependencies: + "bn.js" "^4.1.0" + "elliptic" "^6.5.3" + +"create-hash@^1.1.0", "create-hash@^1.1.2", "create-hash@^1.2.0": + "integrity" "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==" + "resolved" "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "cipher-base" "^1.0.1" + "inherits" "^2.0.1" + "md5.js" "^1.3.4" + "ripemd160" "^2.0.1" + "sha.js" "^2.4.0" + +"create-hmac@^1.1.0", "create-hmac@^1.1.4", "create-hmac@^1.1.7": + "integrity" "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==" + "resolved" "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" + "version" "1.1.7" + dependencies: + "cipher-base" "^1.0.3" + "create-hash" "^1.1.0" + "inherits" "^2.0.1" + "ripemd160" "^2.0.0" + "safe-buffer" "^5.0.1" + "sha.js" "^2.4.8" + +"create-require@^1.1.0": + "integrity" "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + "resolved" "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + "version" "1.1.1" + +"cross-spawn@^7.0.2": + "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" + "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + "version" "7.0.3" + dependencies: + "path-key" "^3.1.0" + "shebang-command" "^2.0.0" + "which" "^2.0.1" + +"crypto-browserify@^3.12.0": + "integrity" "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==" + "resolved" "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz" + "version" "3.12.0" + dependencies: + "browserify-cipher" "^1.0.0" + "browserify-sign" "^4.0.0" + "create-ecdh" "^4.0.0" + "create-hash" "^1.1.0" + "create-hmac" "^1.1.0" + "diffie-hellman" "^5.0.0" + "inherits" "^2.0.1" + "pbkdf2" "^3.0.3" + "public-encrypt" "^4.0.0" + "randombytes" "^2.0.0" + "randomfill" "^1.0.3" + +"crypto-random-string@^2.0.0": + "integrity" "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + "resolved" "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" + "version" "2.0.0" + +"crypto@^1.0.1": + "integrity" "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + "resolved" "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz" + "version" "1.0.1" + +"cssfilter@0.0.10": + "integrity" "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" + "resolved" "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz" + "version" "0.0.10" + +"csv-parse@^4.15.3": + "integrity" "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==" + "resolved" "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz" + "version" "4.16.3" + +"debug@^2.6.9": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" + dependencies: + "ms" "2.0.0" + +"debug@^3.2.7": + "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + "version" "3.2.7" + dependencies: + "ms" "^2.1.1" + +"debug@^4.1.1", "debug@^4.3.0", "debug@^4.3.2", "debug@^4.3.4": + "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + "version" "4.3.4" + dependencies: + "ms" "2.1.2" + +"debug@2.6.9": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" + dependencies: + "ms" "2.0.0" + +"decompress-response@^3.3.0": + "integrity" "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==" + "resolved" "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" + "version" "3.3.0" + dependencies: + "mimic-response" "^1.0.0" + +"deep-extend@^0.6.0": + "integrity" "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + "resolved" "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + "version" "0.6.0" + +"deep-is@^0.1.3": + "integrity" "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + "version" "0.1.4" + +"defer-to-connect@^1.0.1": + "integrity" "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + "resolved" "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" + "version" "1.1.3" + +"define-properties@^1.1.3", "define-properties@^1.1.4": + "integrity" "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==" + "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" + "version" "1.1.4" + dependencies: + "has-property-descriptors" "^1.0.0" + "object-keys" "^1.1.1" + +"delayed-stream@~1.0.0": + "integrity" "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "resolved" "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "version" "1.0.0" + +"depd@~2.0.0", "depd@2.0.0": + "integrity" "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "resolved" "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + "version" "2.0.0" + +"des.js@^1.0.0": + "integrity" "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==" + "resolved" "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "inherits" "^2.0.1" + "minimalistic-assert" "^1.0.0" + +"destroy@1.2.0": + "integrity" "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "resolved" "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" + "version" "1.2.0" + +"diff-sequences@^27.5.1": + "integrity" "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + "resolved" "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" + "version" "27.5.1" + +"diff@^4.0.1": + "integrity" "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + "resolved" "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + "version" "4.0.2" + +"diffie-hellman@^5.0.0": + "integrity" "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==" + "resolved" "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" + "version" "5.0.3" + dependencies: + "bn.js" "^4.1.0" + "miller-rabin" "^4.0.0" + "randombytes" "^2.0.0" + +"dir-glob@^3.0.1": + "integrity" "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==" + "resolved" "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "path-type" "^4.0.0" + +"doctrine@^2.1.0": + "integrity" "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==" + "resolved" "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "esutils" "^2.0.2" + +"doctrine@^3.0.0": + "integrity" "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==" + "resolved" "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "esutils" "^2.0.2" + +"dot-prop@^5.2.0": + "integrity" "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==" + "resolved" "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + "version" "5.3.0" + dependencies: + "is-obj" "^2.0.0" + +"dotenv@^16.0.1": + "integrity" "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" + "resolved" "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz" + "version" "16.0.1" + +"duplexer3@^0.1.4": + "integrity" "sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==" + "resolved" "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" + "version" "0.1.4" + +"ee-first@1.1.1": + "integrity" "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "resolved" "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + "version" "1.1.1" + +"elliptic@^6.5.3": + "integrity" "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==" + "resolved" "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" + "version" "6.5.4" + dependencies: + "bn.js" "^4.11.9" + "brorand" "^1.1.0" + "hash.js" "^1.0.0" + "hmac-drbg" "^1.0.1" + "inherits" "^2.0.4" + "minimalistic-assert" "^1.0.1" + "minimalistic-crypto-utils" "^1.0.1" + +"emoji-regex@^8.0.0": + "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + "version" "8.0.0" + +"encodeurl@~1.0.2": + "integrity" "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + "version" "1.0.2" + +"end-of-stream@^1.1.0": + "integrity" "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==" + "resolved" "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + "version" "1.4.4" + dependencies: + "once" "^1.4.0" + +"es-abstract@^1.19.0", "es-abstract@^1.19.1", "es-abstract@^1.19.2", "es-abstract@^1.19.5": + "integrity" "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==" + "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz" + "version" "1.20.1" + dependencies: + "call-bind" "^1.0.2" + "es-to-primitive" "^1.2.1" + "function-bind" "^1.1.1" + "function.prototype.name" "^1.1.5" + "get-intrinsic" "^1.1.1" + "get-symbol-description" "^1.0.0" + "has" "^1.0.3" + "has-property-descriptors" "^1.0.0" + "has-symbols" "^1.0.3" + "internal-slot" "^1.0.3" + "is-callable" "^1.2.4" + "is-negative-zero" "^2.0.2" + "is-regex" "^1.1.4" + "is-shared-array-buffer" "^1.0.2" + "is-string" "^1.0.7" + "is-weakref" "^1.0.2" + "object-inspect" "^1.12.0" + "object-keys" "^1.1.1" + "object.assign" "^4.1.2" + "regexp.prototype.flags" "^1.4.3" + "string.prototype.trimend" "^1.0.5" + "string.prototype.trimstart" "^1.0.5" + "unbox-primitive" "^1.0.2" + +"es-shim-unscopables@^1.0.0": + "integrity" "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==" + "resolved" "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "has" "^1.0.3" + +"es-to-primitive@^1.2.1": + "integrity" "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==" + "resolved" "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + "version" "1.2.1" + dependencies: + "is-callable" "^1.1.4" + "is-date-object" "^1.0.1" + "is-symbol" "^1.0.2" + +"escape-goat@^2.0.0": + "integrity" "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + "resolved" "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz" + "version" "2.1.1" + +"escape-html@~1.0.3": + "integrity" "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "resolved" "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + "version" "1.0.3" + +"escape-string-regexp@^4.0.0": + "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + "version" "4.0.0" + +"eslint-config-airbnb-base@^15.0.0": + "integrity" "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==" + "resolved" "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz" + "version" "15.0.0" + dependencies: + "confusing-browser-globals" "^1.0.10" + "object.assign" "^4.1.2" + "object.entries" "^1.1.5" + "semver" "^6.3.0" + +"eslint-config-airbnb-typescript@^16.1.0": + "integrity" "sha512-OUaMPZpTOZGKd5tXOjJ9PRU4iYNW/Z5DoHIynjsVK/FpkWdiY5+nxQW6TiJAlLwVI1l53xUOrnlZWtVBVQzuWA==" + "resolved" "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-16.2.0.tgz" + "version" "16.2.0" + dependencies: + "eslint-config-airbnb-base" "^15.0.0" + +"eslint-config-prettier@^8.3.0": + "integrity" "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==" + "resolved" "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz" + "version" "8.5.0" + +"eslint-import-resolver-node@^0.3.6": + "integrity" "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==" + "resolved" "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz" + "version" "0.3.6" + dependencies: + "debug" "^3.2.7" + "resolve" "^1.20.0" + +"eslint-module-utils@^2.7.3": + "integrity" "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==" + "resolved" "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz" + "version" "2.7.3" + dependencies: + "debug" "^3.2.7" + "find-up" "^2.1.0" + +"eslint-plugin-import@^2.25.2", "eslint-plugin-import@^2.25.3", "eslint-plugin-import@^2.25.4": + "integrity" "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==" + "resolved" "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz" + "version" "2.26.0" + dependencies: + "array-includes" "^3.1.4" + "array.prototype.flat" "^1.2.5" + "debug" "^2.6.9" + "doctrine" "^2.1.0" + "eslint-import-resolver-node" "^0.3.6" + "eslint-module-utils" "^2.7.3" + "has" "^1.0.3" + "is-core-module" "^2.8.1" + "is-glob" "^4.0.3" + "minimatch" "^3.1.2" + "object.values" "^1.1.5" + "resolve" "^1.22.0" + "tsconfig-paths" "^3.14.1" + +"eslint-plugin-jest@^26.1.0": + "integrity" "sha512-R3mq1IepnhtsukHQsWxdyKra3OVwYB+N4k8i45ndqSfr8p9KZV6G+EIUt1Z7hzAh4KlsbXG+nCTlNeGFLFLNvA==" + "resolved" "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.4.6.tgz" + "version" "26.4.6" dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-prettier@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@^8.9.0: - version "8.26.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.26.0.tgz#2bcc8836e6c424c4ac26a5674a70d44d84f2181d" - integrity sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg== - dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.15.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -espree@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" - integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" - -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -express-session@^1.17.3: - version "1.17.3" - resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.3.tgz#14b997a15ed43e5949cb1d073725675dd2777f36" - integrity sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw== - dependencies: - cookie "0.4.2" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~2.0.0" - on-headers "~1.0.2" - parseurl "~1.3.3" - safe-buffer "5.2.1" - uid-safe "~2.1.5" - -express@^4.17.2: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -expression-eval@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/expression-eval/-/expression-eval-4.0.0.tgz#d6a07c93e8b33e635710419d4a595d9208b9cc5e" - integrity sha512-YHSnLTyIb9IKaho2IdQbvlei/pElxnGm48UgaXJ1Fe5au95Ck0R9ftm6rHJQuKw3FguZZ4eXVllJFFFc7LX0WQ== - dependencies: - jsep "^0.3.0" - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== - -fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== +"eslint-plugin-prettier@^4.0.0": + "integrity" "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==" + "resolved" "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "prettier-linter-helpers" "^1.0.0" + +"eslint-scope@^5.1.1": + "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + "version" "5.1.1" + dependencies: + "esrecurse" "^4.3.0" + "estraverse" "^4.1.1" + +"eslint-scope@^7.1.1": + "integrity" "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" + "version" "7.1.1" + dependencies: + "esrecurse" "^4.3.0" + "estraverse" "^5.2.0" + +"eslint-utils@^3.0.0": + "integrity" "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==" + "resolved" "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "eslint-visitor-keys" "^2.0.0" + +"eslint-visitor-keys@^2.0.0": + "integrity" "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + "version" "2.1.0" + +"eslint-visitor-keys@^3.3.0": + "integrity" "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" + "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" + "version" "3.3.0" + +"eslint@*", "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^7.32.0 || ^8.2.0", "eslint@^8.9.0", "eslint@>=5", "eslint@>=7.0.0", "eslint@>=7.28.0": + "integrity" "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==" + "resolved" "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz" + "version" "8.16.0" + dependencies: + "@eslint/eslintrc" "^1.3.0" + "@humanwhocodes/config-array" "^0.9.2" + "ajv" "^6.10.0" + "chalk" "^4.0.0" + "cross-spawn" "^7.0.2" + "debug" "^4.3.2" + "doctrine" "^3.0.0" + "escape-string-regexp" "^4.0.0" + "eslint-scope" "^7.1.1" + "eslint-utils" "^3.0.0" + "eslint-visitor-keys" "^3.3.0" + "espree" "^9.3.2" + "esquery" "^1.4.0" + "esutils" "^2.0.2" + "fast-deep-equal" "^3.1.3" + "file-entry-cache" "^6.0.1" + "functional-red-black-tree" "^1.0.1" + "glob-parent" "^6.0.1" + "globals" "^13.15.0" + "ignore" "^5.2.0" + "import-fresh" "^3.0.0" + "imurmurhash" "^0.1.4" + "is-glob" "^4.0.0" + "js-yaml" "^4.1.0" + "json-stable-stringify-without-jsonify" "^1.0.1" + "levn" "^0.4.1" + "lodash.merge" "^4.6.2" + "minimatch" "^3.1.2" + "natural-compare" "^1.4.0" + "optionator" "^0.9.1" + "regexpp" "^3.2.0" + "strip-ansi" "^6.0.1" + "strip-json-comments" "^3.1.0" + "text-table" "^0.2.0" + "v8-compile-cache" "^2.0.3" + +"espree@^9.3.2": + "integrity" "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==" + "resolved" "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz" + "version" "9.3.2" + dependencies: + "acorn" "^8.7.1" + "acorn-jsx" "^5.3.2" + "eslint-visitor-keys" "^3.3.0" + +"esquery@^1.4.0": + "integrity" "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==" + "resolved" "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "estraverse" "^5.1.0" + +"esrecurse@^4.3.0": + "integrity" "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==" + "resolved" "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "estraverse" "^5.2.0" + +"estraverse@^4.1.1": + "integrity" "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + "version" "4.3.0" + +"estraverse@^5.1.0": + "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + "version" "5.3.0" + +"estraverse@^5.2.0": + "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + "version" "5.3.0" + +"esutils@^2.0.2": + "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + "version" "2.0.3" + +"etag@~1.8.1": + "integrity" "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "resolved" "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + "version" "1.8.1" + +"evp_bytestokey@^1.0.0", "evp_bytestokey@^1.0.3": + "integrity" "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==" + "resolved" "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "md5.js" "^1.3.4" + "safe-buffer" "^5.1.1" + +"express-session@^1.17.3": + "integrity" "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==" + "resolved" "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz" + "version" "1.17.3" + dependencies: + "cookie" "0.4.2" + "cookie-signature" "1.0.6" + "debug" "2.6.9" + "depd" "~2.0.0" + "on-headers" "~1.0.2" + "parseurl" "~1.3.3" + "safe-buffer" "5.2.1" + "uid-safe" "~2.1.5" + +"express@^4.17.2": + "integrity" "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==" + "resolved" "https://registry.npmjs.org/express/-/express-4.18.1.tgz" + "version" "4.18.1" + dependencies: + "accepts" "~1.3.8" + "array-flatten" "1.1.1" + "body-parser" "1.20.0" + "content-disposition" "0.5.4" + "content-type" "~1.0.4" + "cookie" "0.5.0" + "cookie-signature" "1.0.6" + "debug" "2.6.9" + "depd" "2.0.0" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "finalhandler" "1.2.0" + "fresh" "0.5.2" + "http-errors" "2.0.0" + "merge-descriptors" "1.0.1" + "methods" "~1.1.2" + "on-finished" "2.4.1" + "parseurl" "~1.3.3" + "path-to-regexp" "0.1.7" + "proxy-addr" "~2.0.7" + "qs" "6.10.3" + "range-parser" "~1.2.1" + "safe-buffer" "5.2.1" + "send" "0.18.0" + "serve-static" "1.15.0" + "setprototypeof" "1.2.0" + "statuses" "2.0.1" + "type-is" "~1.6.18" + "utils-merge" "1.0.1" + "vary" "~1.1.2" + +"expression-eval@^5.0.0": + "integrity" "sha512-2H7OBTa/UKBgTVRRb3/lXd+D89cLjClNtldnzOpZYWZK1zBLIlrz8BLWp5f81AAYOc37GbhkCRXtl5Z/q4D91g==" + "resolved" "https://registry.npmjs.org/expression-eval/-/expression-eval-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "jsep" "^0.3.0" + +"fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": + "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + "version" "3.1.3" + +"fast-diff@^1.1.2": + "integrity" "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + "resolved" "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" + "version" "1.2.0" + +"fast-glob@^3.2.9": + "integrity" "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==" + "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" + "version" "3.2.11" dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - -follow-redirects@^1.14.4: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functions-have-names@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== - dependencies: - type-fest "^0.20.2" - -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== - -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== - dependencies: - has "^1.0.3" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - -jest-matcher-utils@^27.0.0: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== - dependencies: - chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -js-sdsl@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" - integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsep@^0.3.0: - version "0.3.5" - resolved "https://registry.yarnpkg.com/jsep/-/jsep-0.3.5.tgz#3fd79ebd92f6f434e4857d5272aaeef7d948264d" - integrity sha512-AoRLBDc6JNnKjNcmonituEABS5bcfqDhQAWWXNTFrqu6nVXBpBAGfcoTGZMFlIrh9FjmE1CQyX9CTNwZrXMMDA== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lmdb@^2.2.1: - version "2.6.8" - resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.6.8.tgz#4e5f80611ce28b69d09defc891032a4e7631e9d6" - integrity sha512-Vn+o5gFYTE39twmt9gVSDJi8mrWbnkDongSuYdGxhHDHFGX4ct2ITQWMp5THeoEcsQsNK4KhALjW1Hx83x7WGw== - dependencies: - msgpackr "1.7.2" - node-addon-api "^4.3.0" - node-gyp-build-optional-packages "5.0.3" - ordered-binary "^1.4.0" - weak-lru-cache "^1.2.2" + "glob-parent" "^5.1.2" + "merge2" "^1.3.0" + "micromatch" "^4.0.4" + +"fast-json-stable-stringify@^2.0.0": + "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "resolved" "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + "version" "2.1.0" + +"fast-levenshtein@^2.0.6": + "integrity" "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "resolved" "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + "version" "2.0.6" + +"fastq@^1.6.0": + "integrity" "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==" + "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + "version" "1.13.0" + dependencies: + "reusify" "^1.0.4" + +"file-entry-cache@^6.0.1": + "integrity" "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==" + "resolved" "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "flat-cache" "^3.0.4" + +"fill-range@^7.0.1": + "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "to-regex-range" "^5.0.1" + +"finalhandler@1.2.0": + "integrity" "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==" + "resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "debug" "2.6.9" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "on-finished" "2.4.1" + "parseurl" "~1.3.3" + "statuses" "2.0.1" + "unpipe" "~1.0.0" + +"find-up@^2.1.0": + "integrity" "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "locate-path" "^2.0.0" + +"flat-cache@^3.0.4": + "integrity" "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==" + "resolved" "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "flatted" "^3.1.0" + "rimraf" "^3.0.2" + +"flatted@^3.1.0": + "integrity" "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" + "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz" + "version" "3.2.5" + +"follow-redirects@^1.14.4": + "integrity" "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz" + "version" "1.15.1" + +"form-data@^2.5.0": + "integrity" "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==" + "resolved" "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz" + "version" "2.5.1" + dependencies: + "asynckit" "^0.4.0" + "combined-stream" "^1.0.6" + "mime-types" "^2.1.12" + +"forwarded@0.2.0": + "integrity" "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + "version" "0.2.0" + +"fresh@0.5.2": + "integrity" "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "resolved" "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + "version" "0.5.2" + +"fs.realpath@^1.0.0": + "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "version" "1.0.0" + +"fsevents@~2.3.2": + "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + "version" "2.3.2" + +"function-bind@^1.1.1": + "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + "version" "1.1.1" + +"function.prototype.name@^1.1.5": + "integrity" "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==" + "resolved" "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.0" + "functions-have-names" "^1.2.2" + +"functional-red-black-tree@^1.0.1": + "integrity" "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + "resolved" "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" + "version" "1.0.1" + +"functions-have-names@^1.2.2": + "integrity" "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + "resolved" "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + "version" "1.2.3" + +"get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1": + "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==" + "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" + "version" "1.1.1" + dependencies: + "function-bind" "^1.1.1" + "has" "^1.0.3" + "has-symbols" "^1.0.1" + +"get-stream@^4.1.0": + "integrity" "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "pump" "^3.0.0" + +"get-stream@^5.1.0": + "integrity" "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + "version" "5.2.0" + dependencies: + "pump" "^3.0.0" + +"get-symbol-description@^1.0.0": + "integrity" "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==" + "resolved" "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "call-bind" "^1.0.2" + "get-intrinsic" "^1.1.1" + +"glob-parent@^5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob-parent@^6.0.1": + "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + "version" "6.0.2" + dependencies: + "is-glob" "^4.0.3" + +"glob-parent@~5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob@^7.1.3": + "integrity" "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + "version" "7.2.3" + dependencies: + "fs.realpath" "^1.0.0" + "inflight" "^1.0.4" + "inherits" "2" + "minimatch" "^3.1.1" + "once" "^1.3.0" + "path-is-absolute" "^1.0.0" + +"global-dirs@^3.0.0": + "integrity" "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==" + "resolved" "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "ini" "2.0.0" + +"globals@^13.15.0": + "integrity" "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==" + "resolved" "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz" + "version" "13.15.0" + dependencies: + "type-fest" "^0.20.2" + +"globby@^11.1.0": + "integrity" "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==" + "resolved" "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + "version" "11.1.0" + dependencies: + "array-union" "^2.1.0" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.9" + "ignore" "^5.2.0" + "merge2" "^1.4.1" + "slash" "^3.0.0" + +"got@^9.6.0": + "integrity" "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==" + "resolved" "https://registry.npmjs.org/got/-/got-9.6.0.tgz" + "version" "9.6.0" + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + "cacheable-request" "^6.0.0" + "decompress-response" "^3.3.0" + "duplexer3" "^0.1.4" + "get-stream" "^4.1.0" + "lowercase-keys" "^1.0.1" + "mimic-response" "^1.0.1" + "p-cancelable" "^1.0.0" + "to-readable-stream" "^1.0.0" + "url-parse-lax" "^3.0.0" + +"graceful-fs@^4.1.2": + "integrity" "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + "version" "4.2.10" + +"has-bigints@^1.0.1", "has-bigints@^1.0.2": + "integrity" "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + "version" "1.0.2" + +"has-flag@^3.0.0": + "integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + "version" "3.0.0" + +"has-flag@^4.0.0": + "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + "version" "4.0.0" + +"has-property-descriptors@^1.0.0": + "integrity" "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==" + "resolved" "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "get-intrinsic" "^1.1.1" + +"has-symbols@^1.0.1", "has-symbols@^1.0.2", "has-symbols@^1.0.3": + "integrity" "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + "version" "1.0.3" + +"has-tostringtag@^1.0.0": + "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==" + "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "has-symbols" "^1.0.2" + +"has-yarn@^2.1.0": + "integrity" "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + "resolved" "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" + "version" "2.1.0" + +"has@^1.0.3": + "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" + "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "function-bind" "^1.1.1" + +"hash-base@^3.0.0": + "integrity" "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==" + "resolved" "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "inherits" "^2.0.4" + "readable-stream" "^3.6.0" + "safe-buffer" "^5.2.0" + +"hash.js@^1.0.0", "hash.js@^1.0.3", "hash.js@^1.1.3": + "integrity" "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==" + "resolved" "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" + "version" "1.1.7" + dependencies: + "inherits" "^2.0.3" + "minimalistic-assert" "^1.0.1" + +"hmac-drbg@^1.0.1": + "integrity" "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==" + "resolved" "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "hash.js" "^1.0.3" + "minimalistic-assert" "^1.0.0" + "minimalistic-crypto-utils" "^1.0.1" + +"http-cache-semantics@^4.0.0": + "integrity" "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + "resolved" "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" + "version" "4.1.0" + +"http-errors@2.0.0": + "integrity" "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "depd" "2.0.0" + "inherits" "2.0.4" + "setprototypeof" "1.2.0" + "statuses" "2.0.1" + "toidentifier" "1.0.1" + +"iconv-lite@0.4.24": + "integrity" "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==" + "resolved" "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + "version" "0.4.24" + dependencies: + "safer-buffer" ">= 2.1.2 < 3" + +"ignore-by-default@^1.0.1": + "integrity" "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + "resolved" "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" + "version" "1.0.1" + +"ignore@^5.2.0": + "integrity" "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + "version" "5.2.0" + +"import-fresh@^3.0.0", "import-fresh@^3.2.1": + "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" + "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + "version" "3.3.0" + dependencies: + "parent-module" "^1.0.0" + "resolve-from" "^4.0.0" + +"import-lazy@^2.1.0": + "integrity" "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==" + "resolved" "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" + "version" "2.1.0" + +"imurmurhash@^0.1.4": + "integrity" "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + "resolved" "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + "version" "0.1.4" + +"inflight@^1.0.4": + "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" + "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "version" "1.0.6" + dependencies: + "once" "^1.3.0" + "wrappy" "1" + +"inherits@^2.0.1", "inherits@^2.0.3", "inherits@^2.0.4", "inherits@2", "inherits@2.0.4": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" + +"ini@~1.3.0": + "integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "resolved" "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + "version" "1.3.8" + +"ini@2.0.0": + "integrity" "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + "resolved" "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + "version" "2.0.0" + +"internal-slot@^1.0.3": + "integrity" "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==" + "resolved" "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "get-intrinsic" "^1.1.0" + "has" "^1.0.3" + "side-channel" "^1.0.4" + +"ipaddr.js@1.9.1": + "integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + "version" "1.9.1" + +"is-bigint@^1.0.1": + "integrity" "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==" + "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "has-bigints" "^1.0.1" + +"is-binary-path@~2.1.0": + "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "binary-extensions" "^2.0.0" + +"is-boolean-object@^1.1.0": + "integrity" "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==" + "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + "version" "1.1.2" + dependencies: + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" + +"is-callable@^1.1.4", "is-callable@^1.2.4": + "integrity" "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" + "version" "1.2.4" + +"is-ci@^2.0.0": + "integrity" "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==" + "resolved" "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "ci-info" "^2.0.0" + +"is-core-module@^2.8.1": + "integrity" "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==" + "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz" + "version" "2.9.0" + dependencies: + "has" "^1.0.3" + +"is-date-object@^1.0.1": + "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==" + "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "has-tostringtag" "^1.0.0" + +"is-extglob@^2.1.1": + "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + "version" "2.1.1" + +"is-fullwidth-code-point@^3.0.0": + "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + "version" "3.0.0" + +"is-glob@^4.0.0", "is-glob@^4.0.1", "is-glob@^4.0.3", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" + dependencies: + "is-extglob" "^2.1.1" + +"is-installed-globally@^0.4.0": + "integrity" "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==" + "resolved" "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" + "version" "0.4.0" + dependencies: + "global-dirs" "^3.0.0" + "is-path-inside" "^3.0.2" + +"is-negative-zero@^2.0.2": + "integrity" "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + "version" "2.0.2" + +"is-npm@^5.0.0": + "integrity" "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + "resolved" "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz" + "version" "5.0.0" + +"is-number-object@^1.0.4": + "integrity" "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==" + "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + "version" "1.0.7" + dependencies: + "has-tostringtag" "^1.0.0" + +"is-number@^7.0.0": + "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + "version" "7.0.0" + +"is-obj@^2.0.0": + "integrity" "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + "version" "2.0.0" + +"is-path-inside@^3.0.2": + "integrity" "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + "resolved" "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + "version" "3.0.3" + +"is-regex@^1.1.4": + "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==" + "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + "version" "1.1.4" + dependencies: + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" + +"is-shared-array-buffer@^1.0.2": + "integrity" "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==" + "resolved" "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "call-bind" "^1.0.2" + +"is-string@^1.0.5", "is-string@^1.0.7": + "integrity" "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==" + "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + "version" "1.0.7" + dependencies: + "has-tostringtag" "^1.0.0" + +"is-symbol@^1.0.2", "is-symbol@^1.0.3": + "integrity" "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==" + "resolved" "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "has-symbols" "^1.0.2" + +"is-typedarray@^1.0.0": + "integrity" "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "resolved" "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + "version" "1.0.0" + +"is-weakref@^1.0.2": + "integrity" "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==" + "resolved" "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "call-bind" "^1.0.2" + +"is-yarn-global@^0.3.0": + "integrity" "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + "resolved" "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" + "version" "0.3.0" + +"isexe@^2.0.0": + "integrity" "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + "version" "2.0.0" + +"jest-diff@^27.5.1": + "integrity" "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==" + "resolved" "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz" + "version" "27.5.1" + dependencies: + "chalk" "^4.0.0" + "diff-sequences" "^27.5.1" + "jest-get-type" "^27.5.1" + "pretty-format" "^27.5.1" + +"jest-get-type@^27.5.1": + "integrity" "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + "resolved" "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" + "version" "27.5.1" + +"jest-matcher-utils@^27.0.0": + "integrity" "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==" + "resolved" "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz" + "version" "27.5.1" + dependencies: + "chalk" "^4.0.0" + "jest-diff" "^27.5.1" + "jest-get-type" "^27.5.1" + "pretty-format" "^27.5.1" + +"js-yaml@^4.1.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "argparse" "^2.0.1" + +"jsep@^0.3.0": + "integrity" "sha512-AoRLBDc6JNnKjNcmonituEABS5bcfqDhQAWWXNTFrqu6nVXBpBAGfcoTGZMFlIrh9FjmE1CQyX9CTNwZrXMMDA==" + "resolved" "https://registry.npmjs.org/jsep/-/jsep-0.3.5.tgz" + "version" "0.3.5" + +"json-buffer@3.0.0": + "integrity" "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" + "resolved" "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" + "version" "3.0.0" + +"json-schema-traverse@^0.4.1": + "integrity" "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + "version" "0.4.1" + +"json-stable-stringify-without-jsonify@^1.0.1": + "integrity" "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + "resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + "version" "1.0.1" + +"json5@^1.0.1": + "integrity" "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==" + "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "minimist" "^1.2.0" + +"keyv@^3.0.0": + "integrity" "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==" + "resolved" "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "json-buffer" "3.0.0" + +"latest-version@^5.1.0": + "integrity" "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==" + "resolved" "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" + "version" "5.1.0" + dependencies: + "package-json" "^6.3.0" + +"levn@^0.4.1": + "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==" + "resolved" "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + "version" "0.4.1" + dependencies: + "prelude-ls" "^1.2.1" + "type-check" "~0.4.0" + +"lmdb@^2.2.1": + "integrity" "sha512-zhdysJRAy3hftB6zzNO6uqGag5XV4oYMGHuiOsFptZBvpZW6S5FJO1zGnHFeSHF9gBQudD3wI4W0ez6/KURrhg==" + "resolved" "https://registry.npmjs.org/lmdb/-/lmdb-2.4.5.tgz" + "version" "2.4.5" + dependencies: + "msgpackr" "^1.5.4" + "node-addon-api" "^4.3.0" + "node-gyp-build-optional-packages" "5.0.3" + "ordered-binary" "^1.2.4" + "weak-lru-cache" "^1.2.2" optionalDependencies: - "@lmdb/lmdb-darwin-arm64" "2.6.8" - "@lmdb/lmdb-darwin-x64" "2.6.8" - "@lmdb/lmdb-linux-arm" "2.6.8" - "@lmdb/lmdb-linux-arm64" "2.6.8" - "@lmdb/lmdb-linux-x64" "2.6.8" - "@lmdb/lmdb-win32-x64" "2.6.8" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lru-cache@^4.0.3: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -memorystore@^1.6.7: - version "1.6.7" - resolved "https://registry.yarnpkg.com/memorystore/-/memorystore-1.6.7.tgz#78f9b1c2b06949abfb4f85ec71f558ec4265e63d" - integrity sha512-OZnmNY/NDrKohPQ+hxp0muBcBKrzKNtHr55DbqSx9hLsYVNnomSAMRAtI7R64t3gf3ID7tHQA7mG4oL3Hu9hdw== - dependencies: - debug "^4.3.0" - lru-cache "^4.0.3" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== - -morgan@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -msgpackr-extract@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.1.2.tgz#56272030f3e163e1b51964ef8b1cd5e7240c03ed" - integrity sha512-cmrmERQFb19NX2JABOGtrKdHMyI6RUyceaPBQ2iRz9GnDkjBWFjNJC0jyyoOfZl2U/LZE3tQCCQc4dlRyA8mcA== - dependencies: - node-gyp-build-optional-packages "5.0.3" + "@lmdb/lmdb-darwin-arm64" "2.4.5" + "@lmdb/lmdb-darwin-x64" "2.4.5" + "@lmdb/lmdb-linux-arm" "2.4.5" + "@lmdb/lmdb-linux-arm64" "2.4.5" + "@lmdb/lmdb-linux-x64" "2.4.5" + "@lmdb/lmdb-win32-x64" "2.4.5" + +"locate-path@^2.0.0": + "integrity" "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "p-locate" "^2.0.0" + "path-exists" "^3.0.0" + +"lodash.merge@^4.6.2": + "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + "version" "4.6.2" + +"lodash@^4.17.21": + "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + "version" "4.17.21" + +"lowercase-keys@^1.0.0", "lowercase-keys@^1.0.1": + "integrity" "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + "resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" + "version" "1.0.1" + +"lowercase-keys@^2.0.0": + "integrity" "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + "resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" + "version" "2.0.0" + +"lru-cache@^4.0.3": + "integrity" "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" + "version" "4.1.5" + dependencies: + "pseudomap" "^1.0.2" + "yallist" "^2.1.2" + +"lru-cache@^6.0.0": + "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "yallist" "^4.0.0" + +"make-dir@^3.0.0": + "integrity" "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==" + "resolved" "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "semver" "^6.0.0" + +"make-error@^1.1.1": + "integrity" "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "resolved" "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + "version" "1.3.6" + +"md5.js@^1.3.4": + "integrity" "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==" + "resolved" "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" + "version" "1.3.5" + dependencies: + "hash-base" "^3.0.0" + "inherits" "^2.0.1" + "safe-buffer" "^5.1.2" + +"media-typer@0.3.0": + "integrity" "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + "resolved" "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "version" "0.3.0" + +"memorystore@^1.6.7": + "integrity" "sha512-OZnmNY/NDrKohPQ+hxp0muBcBKrzKNtHr55DbqSx9hLsYVNnomSAMRAtI7R64t3gf3ID7tHQA7mG4oL3Hu9hdw==" + "resolved" "https://registry.npmjs.org/memorystore/-/memorystore-1.6.7.tgz" + "version" "1.6.7" + dependencies: + "debug" "^4.3.0" + "lru-cache" "^4.0.3" + +"merge-descriptors@1.0.1": + "integrity" "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "resolved" "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + "version" "1.0.1" + +"merge2@^1.3.0", "merge2@^1.4.1": + "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + "version" "1.4.1" + +"methods@~1.1.2": + "integrity" "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "resolved" "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + "version" "1.1.2" + +"micromatch@^4.0.4": + "integrity" "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==" + "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + "version" "4.0.5" + dependencies: + "braces" "^3.0.2" + "picomatch" "^2.3.1" + +"miller-rabin@^4.0.0": + "integrity" "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==" + "resolved" "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "bn.js" "^4.0.0" + "brorand" "^1.0.1" + +"mime-db@1.52.0": + "integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + "version" "1.52.0" + +"mime-types@^2.1.12", "mime-types@~2.1.24", "mime-types@~2.1.34": + "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + "version" "2.1.35" + dependencies: + "mime-db" "1.52.0" + +"mime@1.6.0": + "integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + "version" "1.6.0" + +"mimic-response@^1.0.0", "mimic-response@^1.0.1": + "integrity" "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + "resolved" "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" + "version" "1.0.1" + +"minimalistic-assert@^1.0.0", "minimalistic-assert@^1.0.1": + "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + "version" "1.0.1" + +"minimalistic-crypto-utils@^1.0.1": + "integrity" "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + "resolved" "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" + "version" "1.0.1" + +"minimatch@^3.0.4", "minimatch@^3.1.1", "minimatch@^3.1.2": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "brace-expansion" "^1.1.7" + +"minimist@^1.2.0", "minimist@^1.2.6": + "integrity" "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" + "version" "1.2.6" + +"morgan@^1.10.0": + "integrity" "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==" + "resolved" "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz" + "version" "1.10.0" + dependencies: + "basic-auth" "~2.0.1" + "debug" "2.6.9" + "depd" "~2.0.0" + "on-finished" "~2.3.0" + "on-headers" "~1.0.2" + +"ms@^2.1.1", "ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" + +"ms@2.0.0": + "integrity" "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "version" "2.0.0" + +"ms@2.1.3": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" + +"msgpackr-extract@^2.0.2": + "integrity" "sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA==" + "resolved" "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "node-gyp-build-optional-packages" "5.0.2" optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.1.2" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.1.2" - "@msgpackr-extract/msgpackr-extract-linux-arm" "2.1.2" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.1.2" - "@msgpackr-extract/msgpackr-extract-linux-x64" "2.1.2" - "@msgpackr-extract/msgpackr-extract-win32-x64" "2.1.2" - -msgpackr@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.7.2.tgz#68d6debf5999d6b61abb6e7046a689991ebf7261" - integrity sha512-mWScyHTtG6TjivXX9vfIy2nBtRupaiAj0HQ2mtmpmYujAmqZmaaEVPaSZ1NKLMvicaMLFzEaMk0ManxMRg8rMQ== + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "2.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "2.0.2" + +"msgpackr@^1.5.4": + "integrity" "sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA==" + "resolved" "https://registry.npmjs.org/msgpackr/-/msgpackr-1.6.1.tgz" + "version" "1.6.1" optionalDependencies: - msgpackr-extract "^2.1.2" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== - -node-gyp-build-optional-packages@5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" - integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== - -nodemon@^2.0.15: - version "2.0.20" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.20.tgz#e3537de768a492e8d74da5c5813cb0c7486fc701" - integrity sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw== - dependencies: - chokidar "^3.5.2" - debug "^3.2.7" - ignore-by-default "^1.0.1" - minimatch "^3.1.2" - pstree.remy "^1.1.8" - semver "^5.7.1" - simple-update-notifier "^1.0.7" - supports-color "^5.5.0" - touch "^3.1.0" - undefsafe "^2.0.5" - -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== - dependencies: - abbrev "1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.2, object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -ordered-binary@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389" - integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ== - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== - -pretty-format@^27.0.0, pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -pstree.remy@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" - integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -random-bytes@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" - integrity sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ== - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve@^1.20.0, resolve@^1.22.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-regex "^1.1.4" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.7: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -semver@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -simple-update-notifier@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz#7edf75c5bdd04f88828d632f762b2bc32996a9cc" - integrity sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew== - dependencies: - semver "~7.0.0" - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -touch@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== - dependencies: - nopt "~1.0.10" + "msgpackr-extract" "^2.0.2" + +"natural-compare@^1.4.0": + "integrity" "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "resolved" "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + "version" "1.4.0" + +"negotiator@0.6.3": + "integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + "version" "0.6.3" + +"node-addon-api@^4.3.0": + "integrity" "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "resolved" "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz" + "version" "4.3.0" + +"node-gyp-build-optional-packages@5.0.2": + "integrity" "sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g==" + "resolved" "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz" + "version" "5.0.2" + +"node-gyp-build-optional-packages@5.0.3": + "integrity" "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==" + "resolved" "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz" + "version" "5.0.3" + +"nodemon@^2.0.15": + "integrity" "sha512-zsrcaOfTWRuUzBn3P44RDliLlp263Z/76FPoHFr3cFFkOz0lTPAcIw8dCzfdVIx/t3AtDYCZRCDkoCojJqaG3w==" + "resolved" "https://registry.npmjs.org/nodemon/-/nodemon-2.0.16.tgz" + "version" "2.0.16" + dependencies: + "chokidar" "^3.5.2" + "debug" "^3.2.7" + "ignore-by-default" "^1.0.1" + "minimatch" "^3.0.4" + "pstree.remy" "^1.1.8" + "semver" "^5.7.1" + "supports-color" "^5.5.0" + "touch" "^3.1.0" + "undefsafe" "^2.0.5" + "update-notifier" "^5.1.0" + +"nopt@~1.0.10": + "integrity" "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=" + "resolved" "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz" + "version" "1.0.10" + dependencies: + "abbrev" "1" + +"normalize-path@^3.0.0", "normalize-path@~3.0.0": + "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + "version" "3.0.0" + +"normalize-url@^4.1.0": + "integrity" "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" + "version" "4.5.1" + +"object-inspect@^1.12.0", "object-inspect@^1.9.0": + "integrity" "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz" + "version" "1.12.2" + +"object-keys@^1.1.1": + "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + "version" "1.1.1" + +"object.assign@^4.1.2": + "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==" + "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "call-bind" "^1.0.0" + "define-properties" "^1.1.3" + "has-symbols" "^1.0.1" + "object-keys" "^1.1.1" + +"object.entries@^1.1.5": + "integrity" "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==" + "resolved" "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"object.values@^1.1.5": + "integrity" "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==" + "resolved" "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"on-finished@~2.3.0": + "integrity" "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" + "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + "version" "2.3.0" + dependencies: + "ee-first" "1.1.1" + +"on-finished@2.4.1": + "integrity" "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==" + "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + "version" "2.4.1" + dependencies: + "ee-first" "1.1.1" + +"on-headers@~1.0.2": + "integrity" "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "resolved" "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + "version" "1.0.2" + +"once@^1.3.0", "once@^1.3.1", "once@^1.4.0": + "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "wrappy" "1" + +"optionator@^0.9.1": + "integrity" "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==" + "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + "version" "0.9.1" + dependencies: + "deep-is" "^0.1.3" + "fast-levenshtein" "^2.0.6" + "levn" "^0.4.1" + "prelude-ls" "^1.2.1" + "type-check" "^0.4.0" + "word-wrap" "^1.2.3" + +"ordered-binary@^1.2.4": + "integrity" "sha512-djRmZoEpOGvIRW7ufsCDHtvcUa18UC9TxnPbHhSVFZHsoyg0dtut1bWtBZ/fmxdPN62oWXrV6adM7NoWU+CneA==" + "resolved" "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.5.tgz" + "version" "1.2.5" + +"p-cancelable@^1.0.0": + "integrity" "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + "resolved" "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" + "version" "1.1.0" + +"p-limit@^1.1.0": + "integrity" "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" + "version" "1.3.0" + dependencies: + "p-try" "^1.0.0" + +"p-locate@^2.0.0": + "integrity" "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "p-limit" "^1.1.0" + +"p-try@^1.0.0": + "integrity" "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "resolved" "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" + "version" "1.0.0" + +"package-json@^6.3.0": + "integrity" "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==" + "resolved" "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" + "version" "6.5.0" + dependencies: + "got" "^9.6.0" + "registry-auth-token" "^4.0.0" + "registry-url" "^5.0.0" + "semver" "^6.2.0" + +"parent-module@^1.0.0": + "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==" + "resolved" "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "callsites" "^3.0.0" + +"parse-asn1@^5.0.0", "parse-asn1@^5.1.5": + "integrity" "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==" + "resolved" "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz" + "version" "5.1.6" + dependencies: + "asn1.js" "^5.2.0" + "browserify-aes" "^1.0.0" + "evp_bytestokey" "^1.0.0" + "pbkdf2" "^3.0.3" + "safe-buffer" "^5.1.1" + +"parseurl@~1.3.3": + "integrity" "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "resolved" "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + "version" "1.3.3" + +"path-exists@^3.0.0": + "integrity" "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + "version" "3.0.0" + +"path-is-absolute@^1.0.0": + "integrity" "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "version" "1.0.1" + +"path-key@^3.1.0": + "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "resolved" "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + "version" "3.1.1" + +"path-parse@^1.0.7": + "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + "version" "1.0.7" + +"path-to-regexp@0.1.7": + "integrity" "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + "version" "0.1.7" + +"path-type@^4.0.0": + "integrity" "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "resolved" "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + "version" "4.0.0" + +"pbkdf2@^3.0.3": + "integrity" "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==" + "resolved" "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "create-hash" "^1.1.2" + "create-hmac" "^1.1.4" + "ripemd160" "^2.0.1" + "safe-buffer" "^5.0.1" + "sha.js" "^2.4.8" + +"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.2.3", "picomatch@^2.3.1": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" + +"prelude-ls@^1.2.1": + "integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + "resolved" "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + "version" "1.2.1" + +"prepend-http@^2.0.0": + "integrity" "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + "resolved" "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" + "version" "2.0.0" + +"prettier-linter-helpers@^1.0.0": + "integrity" "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==" + "resolved" "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "fast-diff" "^1.1.2" + +"prettier@>=2.0.0", "prettier@2.5.1": + "integrity" "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==" + "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz" + "version" "2.5.1" + +"pretty-format@^27.0.0", "pretty-format@^27.5.1": + "integrity" "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==" + "resolved" "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" + "version" "27.5.1" + dependencies: + "ansi-regex" "^5.0.1" + "ansi-styles" "^5.0.0" + "react-is" "^17.0.1" + +"proxy-addr@~2.0.7": + "integrity" "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==" + "resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + "version" "2.0.7" + dependencies: + "forwarded" "0.2.0" + "ipaddr.js" "1.9.1" + +"pseudomap@^1.0.2": + "integrity" "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + "resolved" "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + "version" "1.0.2" + +"pstree.remy@^1.1.8": + "integrity" "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + "resolved" "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz" + "version" "1.1.8" + +"public-encrypt@^4.0.0": + "integrity" "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==" + "resolved" "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" + "version" "4.0.3" + dependencies: + "bn.js" "^4.1.0" + "browserify-rsa" "^4.0.0" + "create-hash" "^1.1.0" + "parse-asn1" "^5.0.0" + "randombytes" "^2.0.1" + "safe-buffer" "^5.1.2" + +"pump@^3.0.0": + "integrity" "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==" + "resolved" "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "end-of-stream" "^1.1.0" + "once" "^1.3.1" + +"punycode@^2.1.0": + "integrity" "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + "version" "2.1.1" + +"pupa@^2.1.1": + "integrity" "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==" + "resolved" "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" + "version" "2.1.1" + dependencies: + "escape-goat" "^2.0.0" + +"qs@6.10.3": + "integrity" "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==" + "resolved" "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz" + "version" "6.10.3" + dependencies: + "side-channel" "^1.0.4" + +"queue-microtask@^1.2.2": + "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + "version" "1.2.3" + +"random-bytes@~1.0.0": + "integrity" "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + "resolved" "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz" + "version" "1.0.0" + +"randombytes@^2.0.0", "randombytes@^2.0.1", "randombytes@^2.0.5": + "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" + "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "safe-buffer" "^5.1.0" + +"randomfill@^1.0.3": + "integrity" "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==" + "resolved" "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "randombytes" "^2.0.5" + "safe-buffer" "^5.1.0" + +"range-parser@~1.2.1": + "integrity" "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + "version" "1.2.1" + +"raw-body@2.5.1": + "integrity" "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==" + "resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" + "version" "2.5.1" + dependencies: + "bytes" "3.1.2" + "http-errors" "2.0.0" + "iconv-lite" "0.4.24" + "unpipe" "1.0.0" + +"rc@^1.2.8": + "integrity" "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==" + "resolved" "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" + "version" "1.2.8" + dependencies: + "deep-extend" "^0.6.0" + "ini" "~1.3.0" + "minimist" "^1.2.0" + "strip-json-comments" "~2.0.1" + +"react-is@^17.0.1": + "integrity" "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "resolved" "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + "version" "17.0.2" + +"readable-stream@^3.6.0": + "integrity" "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + "version" "3.6.0" + dependencies: + "inherits" "^2.0.3" + "string_decoder" "^1.1.1" + "util-deprecate" "^1.0.1" + +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" + dependencies: + "picomatch" "^2.2.1" + +"regexp.prototype.flags@^1.4.3": + "integrity" "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==" + "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" + "version" "1.4.3" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "functions-have-names" "^1.2.2" + +"regexpp@^3.2.0": + "integrity" "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" + "resolved" "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + "version" "3.2.0" + +"registry-auth-token@^4.0.0": + "integrity" "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==" + "resolved" "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" + "version" "4.2.1" + dependencies: + "rc" "^1.2.8" + +"registry-url@^5.0.0": + "integrity" "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==" + "resolved" "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" + "version" "5.1.0" + dependencies: + "rc" "^1.2.8" + +"resolve-from@^4.0.0": + "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + "version" "4.0.0" + +"resolve@^1.20.0", "resolve@^1.22.0": + "integrity" "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" + "version" "1.22.0" + dependencies: + "is-core-module" "^2.8.1" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" + +"responselike@^1.0.2": + "integrity" "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=" + "resolved" "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "lowercase-keys" "^1.0.0" + +"reusify@^1.0.4": + "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + "version" "1.0.4" + +"rimraf@^3.0.2": + "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" + "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "glob" "^7.1.3" + +"ripemd160@^2.0.0", "ripemd160@^2.0.1": + "integrity" "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==" + "resolved" "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "hash-base" "^3.0.0" + "inherits" "^2.0.1" + +"run-parallel@^1.1.9": + "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==" + "resolved" "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "queue-microtask" "^1.2.2" + +"safe-buffer@^5.0.1", "safe-buffer@^5.1.0", "safe-buffer@^5.1.1", "safe-buffer@^5.1.2", "safe-buffer@^5.2.0", "safe-buffer@~5.2.0", "safe-buffer@5.2.1": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" + +"safe-buffer@5.1.2": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" + +"safer-buffer@^2.1.0", "safer-buffer@>= 2.1.2 < 3": + "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "resolved" "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + "version" "2.1.2" + +"semver-diff@^3.1.1": + "integrity" "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==" + "resolved" "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "semver" "^6.3.0" + +"semver@^5.7.1": + "integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + "version" "5.7.1" + +"semver@^6.0.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^6.2.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^6.3.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^7.3.4", "semver@^7.3.7": + "integrity" "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" + "version" "7.3.7" + dependencies: + "lru-cache" "^6.0.0" + +"send@0.18.0": + "integrity" "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==" + "resolved" "https://registry.npmjs.org/send/-/send-0.18.0.tgz" + "version" "0.18.0" + dependencies: + "debug" "2.6.9" + "depd" "2.0.0" + "destroy" "1.2.0" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "fresh" "0.5.2" + "http-errors" "2.0.0" + "mime" "1.6.0" + "ms" "2.1.3" + "on-finished" "2.4.1" + "range-parser" "~1.2.1" + "statuses" "2.0.1" + +"serve-static@1.15.0": + "integrity" "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==" + "resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" + "version" "1.15.0" + dependencies: + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "parseurl" "~1.3.3" + "send" "0.18.0" + +"setprototypeof@1.2.0": + "integrity" "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + "version" "1.2.0" + +"sha.js@^2.4.0", "sha.js@^2.4.8": + "integrity" "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==" + "resolved" "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" + "version" "2.4.11" + dependencies: + "inherits" "^2.0.1" + "safe-buffer" "^5.0.1" + +"shebang-command@^2.0.0": + "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==" + "resolved" "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "shebang-regex" "^3.0.0" + +"shebang-regex@^3.0.0": + "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + "version" "3.0.0" + +"side-channel@^1.0.4": + "integrity" "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==" + "resolved" "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "call-bind" "^1.0.0" + "get-intrinsic" "^1.0.2" + "object-inspect" "^1.9.0" + +"signal-exit@^3.0.2": + "integrity" "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + "version" "3.0.7" + +"slash@^3.0.0": + "integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + "version" "3.0.0" + +"statuses@2.0.1": + "integrity" "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "resolved" "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + "version" "2.0.1" + +"string_decoder@^1.1.1": + "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + "version" "1.3.0" + dependencies: + "safe-buffer" "~5.2.0" + +"string-width@^4.0.0", "string-width@^4.1.0", "string-width@^4.2.2": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" + dependencies: + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" + +"string.prototype.trimend@^1.0.5": + "integrity" "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==" + "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" + +"string.prototype.trimstart@^1.0.5": + "integrity" "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==" + "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" + +"strip-ansi@^6.0.0", "strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "ansi-regex" "^5.0.1" + +"strip-bom@^3.0.0": + "integrity" "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "resolved" "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + "version" "3.0.0" + +"strip-json-comments@^3.1.0", "strip-json-comments@^3.1.1": + "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + "version" "3.1.1" + +"strip-json-comments@~2.0.1": + "integrity" "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + "version" "2.0.1" + +"supports-color@^5.5.0": + "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "has-flag" "^3.0.0" + +"supports-color@^7.1.0": + "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "has-flag" "^4.0.0" + +"supports-preserve-symlinks-flag@^1.0.0": + "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + "version" "1.0.0" + +"text-table@^0.2.0": + "integrity" "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + "version" "0.2.0" + +"to-readable-stream@^1.0.0": + "integrity" "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + "resolved" "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" + "version" "1.0.0" + +"to-regex-range@^5.0.1": + "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "is-number" "^7.0.0" + +"toidentifier@1.0.1": + "integrity" "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + "version" "1.0.1" + +"touch@^3.1.0": + "integrity" "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==" + "resolved" "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "nopt" "~1.0.10" -ts-node@^10.8.0: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== +"ts-node@^10.8.0": + "integrity" "sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA==" + "resolved" "https://registry.npmjs.org/ts-node/-/ts-node-10.8.0.tgz" + "version" "10.8.0" dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + "acorn" "^8.4.1" + "acorn-walk" "^8.1.1" + "arg" "^4.1.0" + "create-require" "^1.1.0" + "diff" "^4.0.1" + "make-error" "^1.1.1" + "v8-compile-cache-lib" "^3.0.1" + "yn" "3.1.1" + +"tsconfig-paths@^3.14.1": + "integrity" "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==" + "resolved" "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz" + "version" "3.14.1" dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typescript@^4.7.2: - version "4.8.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" - integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== - -uid-safe@~2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" - integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== - dependencies: - random-bytes "~1.0.0" - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -undefsafe@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" - integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -weak-lru-cache@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19" - integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw== - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -xss@^1.0.11: - version "1.0.14" - resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.14.tgz#4f3efbde75ad0d82e9921cc3c95e6590dd336694" - integrity sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw== - dependencies: - commander "^2.20.3" - cssfilter "0.0.10" - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + "json5" "^1.0.1" + "minimist" "^1.2.6" + "strip-bom" "^3.0.0" + +"tslib@^1.8.1": + "integrity" "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + "version" "1.14.1" + +"tsutils@^3.21.0": + "integrity" "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==" + "resolved" "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + "version" "3.21.0" + dependencies: + "tslib" "^1.8.1" + +"type-check@^0.4.0", "type-check@~0.4.0": + "integrity" "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==" + "resolved" "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + "version" "0.4.0" + dependencies: + "prelude-ls" "^1.2.1" + +"type-fest@^0.20.2": + "integrity" "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + "version" "0.20.2" + +"type-is@~1.6.18": + "integrity" "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==" + "resolved" "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + "version" "1.6.18" + dependencies: + "media-typer" "0.3.0" + "mime-types" "~2.1.24" + +"typedarray-to-buffer@^3.1.5": + "integrity" "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==" + "resolved" "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + "version" "3.1.5" + dependencies: + "is-typedarray" "^1.0.0" + +"typescript@^4.7.2", "typescript@>=2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": + "integrity" "sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.7.2.tgz" + "version" "4.7.2" + +"uid-safe@~2.1.5": + "integrity" "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==" + "resolved" "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz" + "version" "2.1.5" + dependencies: + "random-bytes" "~1.0.0" + +"unbox-primitive@^1.0.2": + "integrity" "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==" + "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "call-bind" "^1.0.2" + "has-bigints" "^1.0.2" + "has-symbols" "^1.0.3" + "which-boxed-primitive" "^1.0.2" + +"undefsafe@^2.0.5": + "integrity" "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + "resolved" "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" + "version" "2.0.5" + +"unique-string@^2.0.0": + "integrity" "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==" + "resolved" "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "crypto-random-string" "^2.0.0" + +"unpipe@~1.0.0", "unpipe@1.0.0": + "integrity" "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "resolved" "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "version" "1.0.0" + +"update-notifier@^5.1.0": + "integrity" "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==" + "resolved" "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" + "version" "5.1.0" + dependencies: + "boxen" "^5.0.0" + "chalk" "^4.1.0" + "configstore" "^5.0.1" + "has-yarn" "^2.1.0" + "import-lazy" "^2.1.0" + "is-ci" "^2.0.0" + "is-installed-globally" "^0.4.0" + "is-npm" "^5.0.0" + "is-yarn-global" "^0.3.0" + "latest-version" "^5.1.0" + "pupa" "^2.1.1" + "semver" "^7.3.4" + "semver-diff" "^3.1.1" + "xdg-basedir" "^4.0.0" + +"uri-js@^4.2.2": + "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==" + "resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + "version" "4.4.1" + dependencies: + "punycode" "^2.1.0" + +"url-parse-lax@^3.0.0": + "integrity" "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=" + "resolved" "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "prepend-http" "^2.0.0" + +"util-deprecate@^1.0.1": + "integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "version" "1.0.2" + +"utils-merge@1.0.1": + "integrity" "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + "version" "1.0.1" + +"v8-compile-cache-lib@^3.0.1": + "integrity" "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + "resolved" "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + "version" "3.0.1" + +"v8-compile-cache@^2.0.3": + "integrity" "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + "resolved" "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" + "version" "2.3.0" + +"vary@~1.1.2": + "integrity" "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "resolved" "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + "version" "1.1.2" + +"weak-lru-cache@^1.2.2": + "integrity" "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==" + "resolved" "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz" + "version" "1.2.2" + +"which-boxed-primitive@^1.0.2": + "integrity" "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==" + "resolved" "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "is-bigint" "^1.0.1" + "is-boolean-object" "^1.1.0" + "is-number-object" "^1.0.4" + "is-string" "^1.0.5" + "is-symbol" "^1.0.3" + +"which@^2.0.1": + "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" + "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "isexe" "^2.0.0" + +"widest-line@^3.1.0": + "integrity" "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==" + "resolved" "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "string-width" "^4.0.0" + +"word-wrap@^1.2.3": + "integrity" "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "resolved" "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + "version" "1.2.3" + +"wrap-ansi@^7.0.0": + "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + "version" "7.0.0" + dependencies: + "ansi-styles" "^4.0.0" + "string-width" "^4.1.0" + "strip-ansi" "^6.0.0" + +"wrappy@1": + "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" + +"write-file-atomic@^3.0.0": + "integrity" "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==" + "resolved" "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + "version" "3.0.3" + dependencies: + "imurmurhash" "^0.1.4" + "is-typedarray" "^1.0.0" + "signal-exit" "^3.0.2" + "typedarray-to-buffer" "^3.1.5" + +"xdg-basedir@^4.0.0": + "integrity" "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + "resolved" "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" + "version" "4.0.0" + +"xss@^1.0.11": + "integrity" "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==" + "resolved" "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz" + "version" "1.0.11" + dependencies: + "commander" "^2.20.3" + "cssfilter" "0.0.10" + +"yallist@^2.1.2": + "integrity" "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + "version" "2.1.2" + +"yallist@^4.0.0": + "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + "version" "4.0.0" + +"yn@3.1.1": + "integrity" "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + "resolved" "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + "version" "3.1.1" diff --git a/web/frontend/src/components/utils/Endpoints.tsx b/web/frontend/src/components/utils/Endpoints.tsx index 6672cf59d..c46d7fc27 100644 --- a/web/frontend/src/components/utils/Endpoints.tsx +++ b/web/frontend/src/components/utils/Endpoints.tsx @@ -5,7 +5,7 @@ export const ENDPOINT_LOGOUT = '/api/logout'; export const ENDPOINT_USER_RIGHTS = '/api/user_rights'; export const ENDPOINT_ADD_ROLE = '/api/add_role'; export const ENDPOINT_REMOVE_ROLE = '/api/remove_role'; -export const checkTransaction = '/api/evoting/transactions'; +export const checkTransaction = (token: string) => `/api/evoting/transactions/${token}`; export const newForm = '/api/evoting/forms'; export const editForm = (FormID: string) => `/api/evoting/forms/${FormID}`; diff --git a/web/frontend/src/components/utils/usePostCall.tsx b/web/frontend/src/components/utils/usePostCall.tsx index d0bc6ec44..9e2e7f5e0 100644 --- a/web/frontend/src/components/utils/usePostCall.tsx +++ b/web/frontend/src/components/utils/usePostCall.tsx @@ -1,18 +1,36 @@ +import pollTransaction from 'pages/form/components/utils/TransactionPoll'; +import { checkTransaction } from './Endpoints'; + // Custom hook that post a request to an endpoint const usePostCall = (setError) => { return async (endpoint, request, setIsPosting) => { let success = true; - try { - const response = await fetch(endpoint, request); - if (!response.ok) { - const txt = await response.text(); - throw new Error(txt); - } - setError(null); - } catch (error) { - setError(error.message); + const response = await fetch(endpoint, request); + const result = await response.json(); + console.log('result:', result); + + if (!response.ok) { + const txt = await response.text(); + setError(new Error(txt)); success = false; + return success; } + + if (result.Token) { + pollTransaction(checkTransaction, result.Token, 1000, 30).then( + () => { + setIsPosting((prev) => !prev); + console.log('Transaction included'); + }, + (err) => { + console.log('Transaction rejected'); + setError(err.message); + success = false; + } + ); + } + + if (success) setError(null); setIsPosting((prev) => !prev); return success; }; diff --git a/web/frontend/src/pages/form/components/FormForm.tsx b/web/frontend/src/pages/form/components/FormForm.tsx index d8e523091..3f708bf17 100644 --- a/web/frontend/src/pages/form/components/FormForm.tsx +++ b/web/frontend/src/pages/form/components/FormForm.tsx @@ -83,9 +83,9 @@ const FormForm: FC = () => { console.log(response); - console.log(response.TransactionInfo); + console.log(response.Token); - pollTransaction(checkTransaction, response.TransactionInfo, 1000, 30).then( + pollTransaction(checkTransaction, response.Token, 1000, 30).then( () => { setNavigateDestination('/forms/' + response.FormID); setTextModal(`${t('successCreateForm')} ${response.FormID}`); diff --git a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts index b32d7b554..61610e746 100644 --- a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts +++ b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts @@ -1,5 +1,5 @@ const pollTransaction = ( - endpoint: RequestInfo, + endpoint: (token: string) => string, data: any, interval: number, maxAttempts: number @@ -9,27 +9,27 @@ const pollTransaction = ( const request = { method: 'GET', headers: { 'Content-Type': 'application/json' }, - data: data, }; - const executePoll = async (resolve, reject) => { + const executePoll = async (resolve, reject): Promise => { try { attempts += 1; console.log('Request:' + JSON.stringify(request)); - const response = await fetch(endpoint, request); + const response = await fetch(endpoint(data), request); const result = await response.json(); + console.log('Result:' + JSON.stringify(result)); if (!response.ok) { throw new Error(JSON.stringify(result)); } - request.data = result; + data = result.Token; - if (result.Status === '1') { + if (result.Status === 1) { return resolve(result); } - if (result.Status === '2') { + if (result.Status === 2) { throw new Error('Transaction Rejected'); } From e8fa91d39b003bf1ba1da53ca890beaf627e3f4c Mon Sep 17 00:00:00 2001 From: A Date: Sun, 11 Dec 2022 13:03:35 +0100 Subject: [PATCH 29/55] small fixes --- proxy/election.go | 33 +++------------------------------ proxy/mod.go | 3 --- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index e30cf5950..239111094 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -655,11 +655,7 @@ func (h *form) checkHash(status ptypes.TransactionStatus, transactionID []byte, hash.Write([]byte(strconv.FormatInt(Time, 10))) // check if the hash is valid - if !bytes.Equal(hash.Sum(nil), Hash) { - return false - } - - return true + return bytes.Equal(hash.Sum(nil), Hash) } // checkSignature checks if the signature is valid @@ -674,7 +670,7 @@ func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptyp // first get the block idx := lastBlockIdx - for true { + for { blockLink, err := h.blocks.GetByIndex(idx) // if we reached the end of the blockchain @@ -692,29 +688,6 @@ func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptyp idx++ } - - return ptypes.RejectedTransaction, idx - 1 - -} - -// waitForTxnID blocks until `ID` is included or `events` is closed. -func (h *form) waitForTxnID(events <-chan ordering.Event, ID []byte) error { - for event := range events { - for _, res := range event.Transactions { - if !bytes.Equal(res.GetTransaction().GetID(), ID) { - continue - } - - ok, msg := res.GetStatus() - if !ok { - return xerrors.Errorf("transaction %x denied : %s", ID, msg) - } - - return nil - } - } - - return xerrors.New("transaction not found") } func (h *form) getFormsMetadata() (types.FormsMetadata, error) { @@ -904,7 +877,7 @@ func sendResponse(w http.ResponseWriter, response any) error { w.Header().Set("Content-Type", "application/json") // Status et token - + err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, "failed to write in ResponseWriter: "+err.Error(), diff --git a/proxy/mod.go b/proxy/mod.go index 44fd09600..e9fc04d3c 100644 --- a/proxy/mod.go +++ b/proxy/mod.go @@ -10,14 +10,11 @@ import ( "encoding/json" "fmt" "net/http" - "time" "github.com/dedis/d-voting/proxy/types" "go.dedis.ch/kyber/v3/suites" ) -const inclusionTimeout = 10 * time.Second - var suite = suites.MustFind("ed25519") // Form defines the public HTTP API for the form smart contract From 4272e6b2ea7e1a2767eb8f064ec544a8f3345086 Mon Sep 17 00:00:00 2001 From: A Date: Sun, 11 Dec 2022 13:14:14 +0100 Subject: [PATCH 30/55] Comments refactoring Reformating --- proxy/election.go | 38 ++------------------------------------ proxy/types/election.go | 8 ++++++-- web/backend/src/Server.ts | 3 --- 3 files changed, 8 insertions(+), 41 deletions(-) diff --git a/proxy/election.go b/proxy/election.go index 239111094..c056fab51 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -766,55 +766,21 @@ func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, return nil, 0, xerrors.Errorf("failed to create transaction: %v", err) } - //watchCtx, cancel := context.WithTimeout(ctx, inclusionTimeout) //plus besoin de timeout - //defer cancel() - - // events := h.orderingSvc.Watch(watchCtx) il faudra implementer ca lorsque l'on devra appeler checkTxnIncluded - + // get the last block lastBlock, err := h.blocks.Last() if err != nil { return nil, 0, xerrors.Errorf("failed to get last block: %v", err) } lastBlockIdx := lastBlock.GetBlock().GetIndex() - err = h.pool.Add(tx) //dans l'idee, on ajoute la transaction au pool et on sauvegarde le bloc qui debute, - // ensuite on dit au frontend que ca a bien ete added en lui transmettant le txnID - // le frontend peut alors lui meme verifier si la transaction est bien incluse dans le bloc - // en passant par le proxy et sa fonction checkTxnIncluded - + err = h.pool.Add(tx) if err != nil { return nil, 0, xerrors.Errorf("failed to add transaction to the pool: %v", err) } - /* - err = h.waitForTxnID(events, tx.GetID()) - if err != nil { - return nil, xerrors.Errorf("failed to wait for transaction: %v", err) - } - */ return tx.GetID(), lastBlockIdx, nil } -// A function that checks if a transaction is included in a block -/*func (h *form) checkTxnIncluded(events <-chan ordering.Event, ID []byte) (bool, error) { - for event := range events { - for _, res := range event.Transactions { - if !bytes.Equal(res.GetTransaction().GetID(), ID) { - continue - } - - ok, msg := res.GetStatus() - if !ok { - return false, xerrors.Errorf("transaction %x denied : %s", ID, msg) - } - - return true, nil - } - } - - return false, nil -}*/ - func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) error { response, err := h.CreateTransactionInfoToSend(txnID, lastBlockIdx, status) diff --git a/proxy/types/election.go b/proxy/types/election.go index 974ba8600..b2f53f99e 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -12,7 +12,7 @@ const ( UnknownTransactionStatus TransactionStatus = 0 // IncludedTransaction is the status of a transaction that has been included IncludedTransaction TransactionStatus = 1 - // RejectedTransaction is the status of a transaction that is not included and will never be + // RejectedTransaction is the status of a transaction will never be included RejectedTransaction TransactionStatus = 2 ) @@ -59,7 +59,11 @@ type TransactionInfo struct { Signature []byte // signature of the transaction } -// TransactionInfoToSend defines the HTTP response when sending transaction infos to the client +// TransactionInfoToSend defines the HTTP response when sending +// transaction infos to the client so that he can use the status +// of the transaction to know if it has been included or not +// and if it has not been included, he can just use the token +// and ask again later type TransactionInfoToSend struct { Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected Token string diff --git a/web/backend/src/Server.ts b/web/backend/src/Server.ts index 3fae12c10..dc7cebccd 100644 --- a/web/backend/src/Server.ts +++ b/web/backend/src/Server.ts @@ -586,9 +586,6 @@ app.use('/api/evoting/*', (req, res) => { sendToDela(dataStr, req, res); }); - - - // Handles any requests that don't match the ones above app.get('*', (req, res) => { console.log('404 not found'); From 89aee582db3a712490dbe8a70a5391d961cd4297 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 08:24:31 +0100 Subject: [PATCH 31/55] refactoring --- proxy/election.go | 252 ---------------------------------- proxy/transaction.go | 270 +++++++++++++++++++++++++++++++++++++ proxy/types/election.go | 50 +------ proxy/types/transaction.go | 33 +++++ 4 files changed, 308 insertions(+), 297 deletions(-) create mode 100644 proxy/transaction.go create mode 100644 proxy/types/transaction.go diff --git a/proxy/election.go b/proxy/election.go index c056fab51..441197512 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -1,16 +1,12 @@ package proxy import ( - "bytes" - "context" "crypto/sha256" - b64 "encoding/base64" "encoding/hex" "encoding/json" "fmt" //"io" "net/http" - "strconv" "sync" "time" @@ -20,7 +16,6 @@ import ( "github.com/gorilla/mux" "github.com/rs/zerolog" "go.dedis.ch/dela" - "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" "go.dedis.ch/dela/core/txn" @@ -575,121 +570,6 @@ func (h *form) DeleteForm(w http.ResponseWriter, r *http.Request) { h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) } -// IsTxnIncluded -func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - - // check if the formID is valid - if vars == nil || vars["token"] == "" { - http.Error(w, fmt.Sprintf("token not found: %v", vars), http.StatusInternalServerError) - return - } - - token := vars["token"] - - marshall, err := b64.URLEncoding.DecodeString(token) - if err != nil { - http.Error(w, fmt.Sprintf("failed to decode token: %v", err), http.StatusInternalServerError) - return - } - - var content ptypes.TransactionInfo - json.Unmarshal(marshall, &content) - - //h.logger.Info().Msg(fmt.Sprintf("Transaction infos: %+v", content)) - - // get the status of the transaction as byte - if content.Status != ptypes.UnknownTransactionStatus { - http.Error(w, "the transaction status is known", http.StatusBadRequest) - return - } - - // get the signature as a crypto.Signature - signature, err := h.signer.GetSignatureFactory().SignatureOf(h.context, content.Signature) - if err != nil { - http.Error(w, fmt.Sprintf("failed to get Signature: %v", err), http.StatusInternalServerError) - return - } - - // check if the hash is valid - if !h.checkHash(content.Status, content.TransactionID, content.LastBlockIdx, content.Time, content.Hash) { - http.Error(w, "invalid hash", http.StatusInternalServerError) - return - } - - // check if the signature is valid - if !h.checkSignature(content.Hash, signature) { - http.Error(w, "invalid signature", http.StatusInternalServerError) - return - } - - // check if if was submited not to long ago - if time.Now().Unix()-content.Time > int64(maxTimeTransactionCheck) { - http.Error(w, "the transaction is too old", http.StatusInternalServerError) - return - } - - if time.Now().Unix()-content.Time < 0 { - http.Error(w, "the transaction is from the future", http.StatusInternalServerError) - return - } - - // check if the transaction is included in the blockchain - newStatus, idx := h.checkTxnIncluded(content.TransactionID, content.LastBlockIdx) - - err = h.sendTransactionInfo(w, content.TransactionID, idx, newStatus) - if err != nil { - http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) - return - } - -} - -// checkHash checks if the hash is valid -func (h *form) checkHash(status ptypes.TransactionStatus, transactionID []byte, LastBlockIdx uint64, Time int64, Hash []byte) bool { - // create the hash - hash := sha256.New() - hash.Write([]byte{byte(status)}) - hash.Write(transactionID) - hash.Write([]byte(strconv.FormatUint(LastBlockIdx, 10))) - hash.Write([]byte(strconv.FormatInt(Time, 10))) - - // check if the hash is valid - return bytes.Equal(hash.Sum(nil), Hash) -} - -// checkSignature checks if the signature is valid -func (h *form) checkSignature(Hash []byte, Signature crypto.Signature) bool { - // check if the signature is valid - - return h.signer.GetPublicKey().Verify(Hash, Signature) == nil -} - -// checkTxnIncluded checks if the transaction is included in the blockchain -func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptypes.TransactionStatus, uint64) { - // first get the block - idx := lastBlockIdx - - for { - - blockLink, err := h.blocks.GetByIndex(idx) - // if we reached the end of the blockchain - if err != nil { - return ptypes.UnknownTransactionStatus, idx - 1 - } - - transactions := blockLink.GetBlock().GetTransactions() - for _, txn := range transactions { - if bytes.Equal(txn.GetID(), transactionID) { - return ptypes.IncludedTransaction, blockLink.GetBlock().GetIndex() - } - - } - - idx++ - } -} - func (h *form) getFormsMetadata() (types.FormsMetadata, error) { var md types.FormsMetadata @@ -748,135 +628,3 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, return form, nil } -// submitTxn submits a transaction -// Returns the transaction ID. -func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, - cmdArg string, payload []byte) ([]byte, uint64, error) { - - h.Lock() - defer h.Unlock() - - err := h.mngr.Sync() - if err != nil { - return nil, 0, xerrors.Errorf("failed to sync manager: %v", err) - } - - tx, err := createTransaction(h.mngr, cmd, cmdArg, payload) - if err != nil { - return nil, 0, xerrors.Errorf("failed to create transaction: %v", err) - } - - // get the last block - lastBlock, err := h.blocks.Last() - if err != nil { - return nil, 0, xerrors.Errorf("failed to get last block: %v", err) - } - lastBlockIdx := lastBlock.GetBlock().GetIndex() - - err = h.pool.Add(tx) - if err != nil { - return nil, 0, xerrors.Errorf("failed to add transaction to the pool: %v", err) - } - - return tx.GetID(), lastBlockIdx, nil -} - -func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) error { - - response, err := h.CreateTransactionInfoToSend(txnID, lastBlockIdx, status) - if err != nil { - return xerrors.Errorf("failed to create transaction info: %v", err) - } - return sendResponse(w, response) - -} - -func (h *form) CreateTransactionInfoToSend(txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (ptypes.TransactionInfoToSend, error) { - - time := time.Now().Unix() - hash := sha256.New() - - // write status which is a byte to the hash as a []byte - hash.Write([]byte{byte(status)}) - hash.Write(txnID) - hash.Write([]byte(strconv.FormatUint(lastBlockIdx, 10))) - hash.Write([]byte(strconv.FormatInt(time, 10))) - - finalHash := hash.Sum(nil) - - signature, err := h.signer.Sign(finalHash) - - if err != nil { - return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to sign transaction info: %v", err) - } - //convert signature to []byte - signatureBin, err := signature.Serialize(h.context) - if err != nil { - return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal signature: %v", err) - } - - infos := ptypes.TransactionInfo{ - Status: status, - TransactionID: txnID, - LastBlockIdx: lastBlockIdx, - Time: time, - Hash: finalHash, - Signature: signatureBin, - } - marshal, err := json.Marshal(infos) - if err != nil { - return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal transaction info: %v", err) - } - - token := b64.URLEncoding.EncodeToString(marshal) - - response := ptypes.TransactionInfoToSend{ - Status: status, - Token: token, - } - h.logger.Info().Msg(fmt.Sprintf("Transaction info: %v", response)) - return response, nil -} - -func sendResponse(w http.ResponseWriter, response any) error { - - w.Header().Set("Content-Type", "application/json") - - // Status et token - - err := json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, "failed to write in ResponseWriter: "+err.Error(), - http.StatusInternalServerError) - return nil - } - - return nil -} - -// createTransaction creates a transaction with the given command and payload. -func createTransaction(manager txn.Manager, commandType evoting.Command, - commandArg string, buf []byte) (txn.Transaction, error) { - - args := []txn.Arg{ - { - Key: native.ContractArg, - Value: []byte(evoting.ContractName), - }, - { - Key: evoting.CmdArg, - Value: []byte(commandType), - }, - { - Key: commandArg, - Value: buf, - }, - } - - tx, err := manager.Make(args...) - if err != nil { - return nil, xerrors.Errorf("failed to create transaction from manager: %v", err) - } - - return tx, nil -} diff --git a/proxy/transaction.go b/proxy/transaction.go new file mode 100644 index 000000000..5dead9dd6 --- /dev/null +++ b/proxy/transaction.go @@ -0,0 +1,270 @@ +package proxy + +import ( + "bytes" + "context" + "crypto/sha256" + b64 "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "strconv" + "time" + + "github.com/dedis/d-voting/contracts/evoting" + ptypes "github.com/dedis/d-voting/proxy/types" + "github.com/gorilla/mux" + "go.dedis.ch/dela/core/execution/native" + "go.dedis.ch/dela/core/txn" + "go.dedis.ch/dela/crypto" + "golang.org/x/xerrors" +) + +// IsTxnIncluded +func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + // check if the formID is valid + if vars == nil || vars["token"] == "" { + http.Error(w, fmt.Sprintf("token not found: %v", vars), http.StatusInternalServerError) + return + } + + token := vars["token"] + + marshall, err := b64.URLEncoding.DecodeString(token) + if err != nil { + http.Error(w, fmt.Sprintf("failed to decode token: %v", err), http.StatusInternalServerError) + return + } + + var content ptypes.TransactionInfo + json.Unmarshal(marshall, &content) + + //h.logger.Info().Msg(fmt.Sprintf("Transaction infos: %+v", content)) + + // get the status of the transaction as byte + if content.Status != ptypes.UnknownTransactionStatus { + http.Error(w, "the transaction status is known", http.StatusBadRequest) + return + } + + // get the signature as a crypto.Signature + signature, err := h.signer.GetSignatureFactory().SignatureOf(h.context, content.Signature) + if err != nil { + http.Error(w, fmt.Sprintf("failed to get Signature: %v", err), http.StatusInternalServerError) + return + } + + // check if the hash is valid + if !h.checkHash(content.Status, content.TransactionID, content.LastBlockIdx, content.Time, content.Hash) { + http.Error(w, "invalid hash", http.StatusInternalServerError) + return + } + + // check if the signature is valid + if !h.checkSignature(content.Hash, signature) { + http.Error(w, "invalid signature", http.StatusInternalServerError) + return + } + + // check if if was submited not to long ago + if time.Now().Unix()-content.Time > int64(maxTimeTransactionCheck) { + http.Error(w, "the transaction is too old", http.StatusInternalServerError) + return + } + + if time.Now().Unix()-content.Time < 0 { + http.Error(w, "the transaction is from the future", http.StatusInternalServerError) + return + } + + // check if the transaction is included in the blockchain + newStatus, idx := h.checkTxnIncluded(content.TransactionID, content.LastBlockIdx) + + err = h.sendTransactionInfo(w, content.TransactionID, idx, newStatus) + if err != nil { + http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) + return + } + +} + +// checkHash checks if the hash is valid +func (h *form) checkHash(status ptypes.TransactionStatus, transactionID []byte, LastBlockIdx uint64, Time int64, Hash []byte) bool { + // create the hash + hash := sha256.New() + hash.Write([]byte{byte(status)}) + hash.Write(transactionID) + hash.Write([]byte(strconv.FormatUint(LastBlockIdx, 10))) + hash.Write([]byte(strconv.FormatInt(Time, 10))) + + // check if the hash is valid + return bytes.Equal(hash.Sum(nil), Hash) +} + +// checkSignature checks if the signature is valid +func (h *form) checkSignature(Hash []byte, Signature crypto.Signature) bool { + // check if the signature is valid + + return h.signer.GetPublicKey().Verify(Hash, Signature) == nil +} + +// checkTxnIncluded checks if the transaction is included in the blockchain +func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptypes.TransactionStatus, uint64) { + // first get the block + idx := lastBlockIdx + + for { + + blockLink, err := h.blocks.GetByIndex(idx) + // if we reached the end of the blockchain + if err != nil { + return ptypes.UnknownTransactionStatus, idx - 1 + } + + transactions := blockLink.GetBlock().GetTransactions() + for _, txn := range transactions { + if bytes.Equal(txn.GetID(), transactionID) { + return ptypes.IncludedTransaction, blockLink.GetBlock().GetIndex() + } + + } + + idx++ + } +} + + +// submitTxn submits a transaction +// Returns the transaction ID. +func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, + cmdArg string, payload []byte) ([]byte, uint64, error) { + + h.Lock() + defer h.Unlock() + + err := h.mngr.Sync() + if err != nil { + return nil, 0, xerrors.Errorf("failed to sync manager: %v", err) + } + + tx, err := createTransaction(h.mngr, cmd, cmdArg, payload) + if err != nil { + return nil, 0, xerrors.Errorf("failed to create transaction: %v", err) + } + + // get the last block + lastBlock, err := h.blocks.Last() + if err != nil { + return nil, 0, xerrors.Errorf("failed to get last block: %v", err) + } + lastBlockIdx := lastBlock.GetBlock().GetIndex() + + err = h.pool.Add(tx) + if err != nil { + return nil, 0, xerrors.Errorf("failed to add transaction to the pool: %v", err) + } + + return tx.GetID(), lastBlockIdx, nil +} + +func (h *form) sendTransactionInfo(w http.ResponseWriter, txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) error { + + response, err := h.CreateTransactionInfoToSend(txnID, lastBlockIdx, status) + if err != nil { + return xerrors.Errorf("failed to create transaction info: %v", err) + } + return sendResponse(w, response) + +} + +func (h *form) CreateTransactionInfoToSend(txnID []byte, lastBlockIdx uint64, status ptypes.TransactionStatus) (ptypes.TransactionInfoToSend, error) { + + time := time.Now().Unix() + hash := sha256.New() + + // write status which is a byte to the hash as a []byte + hash.Write([]byte{byte(status)}) + hash.Write(txnID) + hash.Write([]byte(strconv.FormatUint(lastBlockIdx, 10))) + hash.Write([]byte(strconv.FormatInt(time, 10))) + + finalHash := hash.Sum(nil) + + signature, err := h.signer.Sign(finalHash) + + if err != nil { + return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to sign transaction info: %v", err) + } + //convert signature to []byte + signatureBin, err := signature.Serialize(h.context) + if err != nil { + return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal signature: %v", err) + } + + infos := ptypes.TransactionInfo{ + Status: status, + TransactionID: txnID, + LastBlockIdx: lastBlockIdx, + Time: time, + Hash: finalHash, + Signature: signatureBin, + } + marshal, err := json.Marshal(infos) + if err != nil { + return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal transaction info: %v", err) + } + + token := b64.URLEncoding.EncodeToString(marshal) + + response := ptypes.TransactionInfoToSend{ + Status: status, + Token: token, + } + h.logger.Info().Msg(fmt.Sprintf("Transaction info: %v", response)) + return response, nil +} + +func sendResponse(w http.ResponseWriter, response any) error { + + w.Header().Set("Content-Type", "application/json") + + // Status et token + + err := json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, "failed to write in ResponseWriter: "+err.Error(), + http.StatusInternalServerError) + return nil + } + + return nil +} + +// createTransaction creates a transaction with the given command and payload. +func createTransaction(manager txn.Manager, commandType evoting.Command, + commandArg string, buf []byte) (txn.Transaction, error) { + + args := []txn.Arg{ + { + Key: native.ContractArg, + Value: []byte(evoting.ContractName), + }, + { + Key: evoting.CmdArg, + Value: []byte(commandType), + }, + { + Key: commandArg, + Value: buf, + }, + } + + tx, err := manager.Make(args...) + if err != nil { + return nil, xerrors.Errorf("failed to create transaction from manager: %v", err) + } + + return tx, nil +} diff --git a/proxy/types/election.go b/proxy/types/election.go index b2f53f99e..ae1cd35e4 100644 --- a/proxy/types/election.go +++ b/proxy/types/election.go @@ -4,22 +4,6 @@ import ( etypes "github.com/dedis/d-voting/contracts/evoting/types" ) -// TransactionStatus is the status of a transaction -type TransactionStatus byte - -const ( - // UnknownTransactionStatus is the basic status of a transaction - UnknownTransactionStatus TransactionStatus = 0 - // IncludedTransaction is the status of a transaction that has been included - IncludedTransaction TransactionStatus = 1 - // RejectedTransaction is the status of a transaction will never be included - RejectedTransaction TransactionStatus = 2 -) - - - - - // CreateFormRequest defines the HTTP request for creating a form type CreateFormRequest struct { AdminID string @@ -29,7 +13,7 @@ type CreateFormRequest struct { // CreateFormResponse defines the HTTP response when creating a form type CreateFormResponse struct { FormID string // hex-encoded - Token string + Token string } // CastVoteRequest defines the HTTP request for casting a vote @@ -39,7 +23,6 @@ type CastVoteRequest struct { Ballot CiphervoteJSON } - // CiphervoteJSON is the JSON representation of a ciphervote type CiphervoteJSON []EGPairJSON @@ -49,29 +32,6 @@ type EGPairJSON struct { C []byte } -// TransactionInfo defines the information of a transaction -type TransactionInfo struct { - Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected - TransactionID []byte - LastBlockIdx uint64 // last block of the chain when the transaction was added to the pool - Time int64 // time when the transaction was added to the pool - Hash []byte // signature of the transaction - Signature []byte // signature of the transaction -} - -// TransactionInfoToSend defines the HTTP response when sending -// transaction infos to the client so that he can use the status -// of the transaction to know if it has been included or not -// and if it has not been included, he can just use the token -// and ask again later -type TransactionInfoToSend struct { - Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected - Token string -} - - - - // UpdateFormRequest defines the HTTP request for updating a form type UpdateFormRequest struct { Action string @@ -80,7 +40,7 @@ type UpdateFormRequest struct { // GetFormResponse defines the HTTP response when getting the form info type GetFormResponse struct { // FormID is hex-encoded - FormID string + FormID string Configuration etypes.Configuration Status uint16 Pubkey string @@ -94,9 +54,9 @@ type GetFormResponse struct { // LightForm represents a light version of the form type LightForm struct { FormID string - Title string - Status uint16 - Pubkey string + Title string + Status uint16 + Pubkey string } // GetFormsResponse defines the HTTP response when getting all forms diff --git a/proxy/types/transaction.go b/proxy/types/transaction.go new file mode 100644 index 000000000..44e8420ec --- /dev/null +++ b/proxy/types/transaction.go @@ -0,0 +1,33 @@ +package types + +// TransactionStatus is the status of a transaction +type TransactionStatus byte + +const ( + // UnknownTransactionStatus is the basic status of a transaction + UnknownTransactionStatus TransactionStatus = 0 + // IncludedTransaction is the status of a transaction that has been included + IncludedTransaction TransactionStatus = 1 + // RejectedTransaction is the status of a transaction will never be included + RejectedTransaction TransactionStatus = 2 +) + +// TransactionInfo defines the information of a transaction +type TransactionInfo struct { + Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected + TransactionID []byte + LastBlockIdx uint64 // last block of the chain when the transaction was added to the pool + Time int64 // time when the transaction was added to the pool + Hash []byte // signature of the transaction + Signature []byte // signature of the transaction +} + +// TransactionInfoToSend defines the HTTP response when sending +// transaction infos to the client so that he can use the status +// of the transaction to know if it has been included or not +// and if it has not been included, he can just use the token +// and ask again later +type TransactionInfoToSend struct { + Status TransactionStatus // 0 if not yet included, 1 if included, 2 if rejected + Token string +} From 3966c7229d13df4231da6cdc5013ae60ec16b4a3 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 08:39:17 +0100 Subject: [PATCH 32/55] comments and refactoring --- proxy/transaction.go | 46 +++++++++++++------ .../src/components/utils/usePostCall.tsx | 1 - .../form/components/utils/TransactionPoll.ts | 4 +- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/proxy/transaction.go b/proxy/transaction.go index 5dead9dd6..b97938bc0 100644 --- a/proxy/transaction.go +++ b/proxy/transaction.go @@ -21,10 +21,12 @@ import ( ) // IsTxnIncluded +// Check if the transaction is included in the blockchain func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { + // get the token from the url vars := mux.Vars(r) - // check if the formID is valid + // check if the token is valid if vars == nil || vars["token"] == "" { http.Error(w, fmt.Sprintf("token not found: %v", vars), http.StatusInternalServerError) return @@ -32,18 +34,23 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { token := vars["token"] + // decode the token marshall, err := b64.URLEncoding.DecodeString(token) if err != nil { http.Error(w, fmt.Sprintf("failed to decode token: %v", err), http.StatusInternalServerError) return } + // unmarshall the token to get the json with all the informations var content ptypes.TransactionInfo - json.Unmarshal(marshall, &content) - - //h.logger.Info().Msg(fmt.Sprintf("Transaction infos: %+v", content)) + err = json.Unmarshal(marshall, &content) + if err != nil { + http.Error(w, fmt.Sprintf("failed to unmarshall token: %v", err), http.StatusInternalServerError) + return + } - // get the status of the transaction as byte + // check if the transaction status is unknown + // if it is not unknown, it means that the transaction was already checked if content.Status != ptypes.UnknownTransactionStatus { http.Error(w, "the transaction status is known", http.StatusBadRequest) return @@ -70,10 +77,16 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { // check if if was submited not to long ago if time.Now().Unix()-content.Time > int64(maxTimeTransactionCheck) { - http.Error(w, "the transaction is too old", http.StatusInternalServerError) + // if it was submited to long ago, we reject the transaction + err = h.sendTransactionInfo(w, content.TransactionID, 0, ptypes.RejectedTransaction) + if err != nil { + http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) + return + } return } + // check if the transaction time stamp is possible if time.Now().Unix()-content.Time < 0 { http.Error(w, "the transaction is from the future", http.StatusInternalServerError) return @@ -82,6 +95,7 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { // check if the transaction is included in the blockchain newStatus, idx := h.checkTxnIncluded(content.TransactionID, content.LastBlockIdx) + // send the transaction info err = h.sendTransactionInfo(w, content.TransactionID, idx, newStatus) if err != nil { http.Error(w, fmt.Sprintf("failed to send transaction info: %v", err), http.StatusInternalServerError) @@ -105,24 +119,26 @@ func (h *form) checkHash(status ptypes.TransactionStatus, transactionID []byte, // checkSignature checks if the signature is valid func (h *form) checkSignature(Hash []byte, Signature crypto.Signature) bool { - // check if the signature is valid - return h.signer.GetPublicKey().Verify(Hash, Signature) == nil } // checkTxnIncluded checks if the transaction is included in the blockchain func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptypes.TransactionStatus, uint64) { - // first get the block + // we start at the last block index + // which is the index of the last block that was checked + // or the last block before the transaction was submited idx := lastBlockIdx for { - + // first get the block blockLink, err := h.blocks.GetByIndex(idx) + // if we reached the end of the blockchain if err != nil { return ptypes.UnknownTransactionStatus, idx - 1 } + // check if the transaction is in the block transactions := blockLink.GetBlock().GetTransactions() for _, txn := range transactions { if bytes.Equal(txn.GetID(), transactionID) { @@ -135,7 +151,6 @@ func (h *form) checkTxnIncluded(transactionID []byte, lastBlockIdx uint64) (ptyp } } - // submitTxn submits a transaction // Returns the transaction ID. func (h *form) submitTxn(ctx context.Context, cmd evoting.Command, @@ -184,7 +199,7 @@ func (h *form) CreateTransactionInfoToSend(txnID []byte, lastBlockIdx uint64, st time := time.Now().Unix() hash := sha256.New() - // write status which is a byte to the hash as a []byte + // create the hash hash.Write([]byte{byte(status)}) hash.Write(txnID) hash.Write([]byte(strconv.FormatUint(lastBlockIdx, 10))) @@ -192,11 +207,13 @@ func (h *form) CreateTransactionInfoToSend(txnID []byte, lastBlockIdx uint64, st finalHash := hash.Sum(nil) + // sign the hash signature, err := h.signer.Sign(finalHash) if err != nil { return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to sign transaction info: %v", err) } + //convert signature to []byte signatureBin, err := signature.Serialize(h.context) if err != nil { @@ -211,18 +228,19 @@ func (h *form) CreateTransactionInfoToSend(txnID []byte, lastBlockIdx uint64, st Hash: finalHash, Signature: signatureBin, } + marshal, err := json.Marshal(infos) if err != nil { return ptypes.TransactionInfoToSend{}, xerrors.Errorf("failed to marshal transaction info: %v", err) } + // encode the transaction info so that the client just has to send it back token := b64.URLEncoding.EncodeToString(marshal) response := ptypes.TransactionInfoToSend{ Status: status, Token: token, } - h.logger.Info().Msg(fmt.Sprintf("Transaction info: %v", response)) return response, nil } @@ -230,8 +248,6 @@ func sendResponse(w http.ResponseWriter, response any) error { w.Header().Set("Content-Type", "application/json") - // Status et token - err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, "failed to write in ResponseWriter: "+err.Error(), diff --git a/web/frontend/src/components/utils/usePostCall.tsx b/web/frontend/src/components/utils/usePostCall.tsx index 9e2e7f5e0..16b249472 100644 --- a/web/frontend/src/components/utils/usePostCall.tsx +++ b/web/frontend/src/components/utils/usePostCall.tsx @@ -7,7 +7,6 @@ const usePostCall = (setError) => { let success = true; const response = await fetch(endpoint, request); const result = await response.json(); - console.log('result:', result); if (!response.ok) { const txt = await response.text(); diff --git a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts index 61610e746..dabee9dd3 100644 --- a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts +++ b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts @@ -14,11 +14,9 @@ const pollTransaction = ( const executePoll = async (resolve, reject): Promise => { try { attempts += 1; - console.log('Request:' + JSON.stringify(request)); const response = await fetch(endpoint(data), request); const result = await response.json(); - console.log('Result:' + JSON.stringify(result)); - + if (!response.ok) { throw new Error(JSON.stringify(result)); } From d980543a32010a3b51d1801d9d7f58892fe9824d Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 08:41:51 +0100 Subject: [PATCH 33/55] reformat --- web/frontend/src/pages/form/components/utils/TransactionPoll.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts index dabee9dd3..1aa4c03f8 100644 --- a/web/frontend/src/pages/form/components/utils/TransactionPoll.ts +++ b/web/frontend/src/pages/form/components/utils/TransactionPoll.ts @@ -16,7 +16,7 @@ const pollTransaction = ( attempts += 1; const response = await fetch(endpoint(data), request); const result = await response.json(); - + if (!response.ok) { throw new Error(JSON.stringify(result)); } From e26f4a83b2354f99be12f465547eacc1fb6a4156 Mon Sep 17 00:00:00 2001 From: A Date: Sun, 13 Nov 2022 11:53:51 +0100 Subject: [PATCH 34/55] Created new integration tests --- integration/integration_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration/integration_test.go b/integration/integration_test.go index 95db01e83..16d411620 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -156,6 +156,8 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { //remove the casted vote from the list castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) ok = true + //remove the casted vote from the list + castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) break } } From d8fd2ad41f09d3900f9a7884c3ade2f86f40ff94 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 14 Nov 2022 10:50:41 +0100 Subject: [PATCH 35/55] fix comment --- integration/integration_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integration/integration_test.go b/integration/integration_test.go index 16d411620..863b5da64 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -263,7 +263,11 @@ func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing t.Logf("shuffling") err = sActor.Shuffle(formID) +<<<<<<< HEAD // If the number of failing nodes is greater +======= + // If the number of failing nodes is greater +>>>>>>> 9242a4a (fix comment) // than the threshold, the shuffle will fail fmt.Println("threshold: ", numNodes/3) if failingNodes > numNodes/3 { From 7f5d41fd40d97f7fdbaa042e15a5e804c1948b38 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 21 Nov 2022 10:05:38 +0100 Subject: [PATCH 36/55] load test and split files --- integration/utility_functions.go | 524 +++++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 integration/utility_functions.go diff --git a/integration/utility_functions.go b/integration/utility_functions.go new file mode 100644 index 000000000..43bf862d8 --- /dev/null +++ b/integration/utility_functions.go @@ -0,0 +1,524 @@ +package integration + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "io" + "math/rand" + "strconv" + "strings" + "sync" + "time" + + "github.com/dedis/d-voting/contracts/evoting" + "github.com/dedis/d-voting/contracts/evoting/types" + "github.com/dedis/d-voting/internal/testing/fake" + "github.com/dedis/d-voting/services/dkg" + "github.com/dedis/d-voting/services/shuffle" + "go.dedis.ch/dela/core/execution/native" + "go.dedis.ch/dela/core/ordering" + "go.dedis.ch/dela/core/txn" + "go.dedis.ch/dela/core/txn/signed" + "go.dedis.ch/dela/crypto" + "go.dedis.ch/dela/serde" + "go.dedis.ch/dela/serde/json" + "go.dedis.ch/kyber/v3" + "golang.org/x/xerrors" +) + +const addAndWaitErr = "failed to addAndWait: %v" + +var serdecontext = json.NewContext() + +func ballotIsNull(ballot types.Ballot) bool { + return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && + ballot.RankResultIDs == nil && ballot.RankResult == nil && + ballot.TextResultIDs == nil && ballot.TextResult == nil +} + +func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, + timeout time.Duration, retry int) txManager { + + client := client{ + srvc: firstNode.GetOrdering(), + mgr: firstNode.GetValidationSrv(), + } + + return txManager{ + m: signed.NewManager(signer, client), + n: firstNode, + t: timeout, + retry: retry, + } +} + +type txManager struct { + m txn.Manager + n dVotingCosiDela + t time.Duration + retry int +} + +func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { + for i := 0; i < m.retry; i++ { + sentTxn, err := m.m.Make(args...) + if err != nil { + return nil, xerrors.Errorf("failed to Make: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), m.t) + defer cancel() + + events := m.n.GetOrdering().Watch(ctx) + + err = m.n.GetPool().Add(sentTxn) + if err != nil { + return nil, xerrors.Errorf("failed to Add: %v", err) + } + + sentTxnID := sentTxn.GetID() + + accepted := isAccepted(events, sentTxnID) + if accepted { + return sentTxnID, nil + } + + err = m.m.Sync() + if err != nil { + return nil, xerrors.Errorf("failed to sync: %v", err) + } + + cancel() + } + + return nil, xerrors.Errorf("transaction not included after timeout: %v", args) +} + +// isAccepted returns true if the transaction was included then accepted +func isAccepted(events <-chan ordering.Event, txID []byte) bool { + for event := range events { + for _, result := range event.Transactions { + fetchedTxnID := result.GetTransaction().GetID() + + if bytes.Equal(txID, fetchedTxnID) { + accepted, _ := event.Transactions[0].GetStatus() + + return accepted + } + } + } + + return false +} + +func grantAccess(m txManager, signer crypto.Signer) error { + pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() + if err != nil { + return xerrors.Errorf("failed to GetPublicKey: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, + {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, + {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, + {Key: "access:grant_command", Value: []byte("all")}, + {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, + {Key: "access:command", Value: []byte("GRANT")}, + } + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf("failed to grantAccess: %v", err) + } + + return nil +} + +func createForm(m txManager, title string, admin string) ([]byte, error) { + // Define the configuration : + configuration := fake.BasicConfiguration + + createForm := types.CreateForm{ + Configuration: configuration, + AdminID: admin, + } + + data, err := createForm.Serialize(serdecontext) + if err != nil { + return nil, xerrors.Errorf("failed to serialize: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCreateForm)}, + } + + txID, err := m.addAndWait(args...) + if err != nil { + return nil, xerrors.Errorf(addAndWaitErr, err) + } + + // Calculate formID from + hash := sha256.New() + hash.Write(txID) + formID := hash.Sum(nil) + + return formID, nil +} + +func openForm(m txManager, formID []byte) error { + openForm := &types.OpenForm{ + FormID: hex.EncodeToString(formID), + } + + data, err := openForm.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize open form: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdOpenForm)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + return nil +} + +func getForm(formFac serde.Factory, formID []byte, + service ordering.Service) (types.Form, error) { + + form := types.Form{} + + proof, err := service.GetProof(formID) + if err != nil { + return form, xerrors.Errorf("failed to GetProof: %v", err) + } + + if proof == nil { + return form, xerrors.Errorf("form does not exist: %v", err) + } + + message, err := formFac.Deserialize(serdecontext, proof.GetValue()) + if err != nil { + return form, xerrors.Errorf("failed to deserialize Form: %v", err) + } + + form, ok := message.(types.Form) + if !ok { + return form, xerrors.Errorf("wrong message type: %T", message) + } + + return form, nil +} + +func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, + numberOfVotes int) ([]types.Ballot, error) { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":0,0,1,0\n" + + "text:" + encodeID("ee") + ":eWVz\n\n"), //encoding of "yes" + string("select:" + encodeID("bb") + ":1,1,0,0\n" + + "text:" + encodeID("ee") + ":amE=\n\n"), //encoding of "ja + string("select:" + encodeID("bb") + ":0,0,0,1\n" + + "text:" + encodeID("ee") + ":b3Vp\n\n"), //encoding of "oui" + } + + votes := make([]types.Ballot, numberOfVotes) + + for i := 0; i < numberOfVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return nil, xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "user " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return nil, xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return nil, xerrors.Errorf(addAndWaitErr, err) + } + + var ballot types.Ballot + err = ballot.Unmarshal(vote, form) + if err != nil { + return nil, xerrors.Errorf("failed to unmarshal ballot: %v", err) + } + + votes[i] = ballot + } + + return votes, nil +} + +func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":1,0,1,1\n" + + "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" + string("select:" + encodeID("bb") + ":1,1,1,1\n" + + "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" + + } + + for i := 0; i < numberOfBadVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "badUser " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + //votes[i] = ballot + } + + return nil +} + +func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { + + var ballot = make([]types.EGPair, chunks) + + buf := make([]byte, 29) + + for i := 0; i < chunks; i++ { + var K, C kyber.Point + var err error + + n, err := vote.Read(buf) + if err != nil { + return nil, xerrors.Errorf("failed to read: %v", err) + } + + K, C, _, err = actor.Encrypt(buf[:n]) + if err != nil { + return types.Ciphervote{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) + } + + ballot[i] = types.EGPair{ + K: K, + C: C, + } + } + + return ballot, nil +} + +func closeForm(m txManager, formID []byte, admin string) error { + closeForm := &types.CloseForm{ + FormID: hex.EncodeToString(formID), + UserID: admin, + } + + data, err := closeForm.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize open form: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCloseForm)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf("failed to Marshall closeForm: %v", err) + } + + return nil +} + +func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { + var actor dkg.Actor + var err error + + for _, node := range nodes { + d := node.(dVotingNode).GetDkg() + + // put Listen in a goroutine to optimize for speed + actor, err = d.Listen(formID, m) + if err != nil { + return nil, xerrors.Errorf("failed to GetDkg: %v", err) + } + } + + _, err = actor.Setup() + if err != nil { + return nil, xerrors.Errorf("failed to Setup: %v", err) + } + + return actor, nil +} + +func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { + var sActor shuffle.Actor + + for _, node := range nodes { + client := client{ + srvc: node.GetOrdering(), + mgr: node.GetValidationSrv(), + } + + var err error + shuffler := node.GetShuffle() + + sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) + if err != nil { + return nil, xerrors.Errorf("failed to init Shuffle: %v", err) + } + } + + return sActor, nil +} + +func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { + if form.Status != types.PubSharesSubmitted { + return xerrors.Errorf("cannot decrypt: not all pubShares submitted") + } + + decryptBallots := types.CombineShares{ + FormID: form.FormID, + } + + data, err := decryptBallots.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize ballots: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCombineShares)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + return nil +} + +func closeNodes(nodes []dVotingCosiDela) error { + wait := sync.WaitGroup{} + wait.Add(len(nodes)) + + for _, n := range nodes { + go func(node dVotingNode) { + defer wait.Done() + node.GetOrdering().Close() + }(n.(dVotingNode)) + } + + done := make(chan struct{}) + + go func() { + wait.Wait() + close(done) + }() + + select { + case <-done: + return nil + case <-time.After(time.Second * 30): + return xerrors.New("failed to close: timeout") + } +} + +func encodeID(ID string) types.ID { + return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) +} + +// waitForStatus polls the nodes until they all updated to the expected status +// for the given form. An error is raised if the timeout expires. +func waitForStatus(status types.Status, formFac types.FormFactory, + formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { + + expiration := time.Now().Add(timeOut) + + isOK := func() (bool, error) { + for _, node := range nodes { + form, err := getForm(formFac, formID, node.GetOrdering()) + if err != nil { + return false, xerrors.Errorf("failed to get form: %v", err) + } + + if form.Status != status { + return false, nil + } + } + + return true, nil + } + + for { + if time.Now().After(expiration) { + return xerrors.New("status check expired") + } + + ok, err := isOK() + if err != nil { + return xerrors.Errorf("failed to check status: %v", err) + } + + if ok { + return nil + } + + time.Sleep(time.Millisecond * 100) + } +} From eb7385d9948a41dd3610b72bc669f7d3dcb235dd Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:15:30 +0100 Subject: [PATCH 37/55] add new files --- integration/dkg_utility_functions.go | 29 +++ ...functions.go => form_utility_functions.go} | 223 +----------------- integration/nodes_utility_functions.go | 76 ++++++ integration/shuffle_utility_functions.go | 28 +++ integration/transaction_utility_functions.go | 115 +++++++++ 5 files changed, 252 insertions(+), 219 deletions(-) create mode 100644 integration/dkg_utility_functions.go rename integration/{utility_functions.go => form_utility_functions.go} (61%) create mode 100644 integration/nodes_utility_functions.go create mode 100644 integration/shuffle_utility_functions.go create mode 100644 integration/transaction_utility_functions.go diff --git a/integration/dkg_utility_functions.go b/integration/dkg_utility_functions.go new file mode 100644 index 000000000..8a583fa19 --- /dev/null +++ b/integration/dkg_utility_functions.go @@ -0,0 +1,29 @@ +package integration + +import ( + "github.com/dedis/d-voting/services/dkg" + "go.dedis.ch/dela/core/txn" + "golang.org/x/xerrors" +) + +func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { + var actor dkg.Actor + var err error + + for _, node := range nodes { + d := node.(dVotingNode).GetDkg() + + // put Listen in a goroutine to optimize for speed + actor, err = d.Listen(formID, m) + if err != nil { + return nil, xerrors.Errorf("failed to GetDkg: %v", err) + } + } + + _, err = actor.Setup() + if err != nil { + return nil, xerrors.Errorf("failed to Setup: %v", err) + } + + return actor, nil +} diff --git a/integration/utility_functions.go b/integration/form_utility_functions.go similarity index 61% rename from integration/utility_functions.go rename to integration/form_utility_functions.go index 43bf862d8..87029942a 100644 --- a/integration/utility_functions.go +++ b/integration/form_utility_functions.go @@ -1,8 +1,6 @@ package integration import ( - "bytes" - "context" "crypto/sha256" "encoding/base64" "encoding/hex" @@ -10,132 +8,32 @@ import ( "math/rand" "strconv" "strings" - "sync" - "time" "github.com/dedis/d-voting/contracts/evoting" "github.com/dedis/d-voting/contracts/evoting/types" "github.com/dedis/d-voting/internal/testing/fake" "github.com/dedis/d-voting/services/dkg" - "github.com/dedis/d-voting/services/shuffle" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/serde" "go.dedis.ch/dela/serde/json" "go.dedis.ch/kyber/v3" "golang.org/x/xerrors" ) -const addAndWaitErr = "failed to addAndWait: %v" - var serdecontext = json.NewContext() +func encodeID(ID string) types.ID { + return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) +} + func ballotIsNull(ballot types.Ballot) bool { return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && ballot.RankResultIDs == nil && ballot.RankResult == nil && ballot.TextResultIDs == nil && ballot.TextResult == nil } -func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, - timeout time.Duration, retry int) txManager { - - client := client{ - srvc: firstNode.GetOrdering(), - mgr: firstNode.GetValidationSrv(), - } - - return txManager{ - m: signed.NewManager(signer, client), - n: firstNode, - t: timeout, - retry: retry, - } -} - -type txManager struct { - m txn.Manager - n dVotingCosiDela - t time.Duration - retry int -} - -func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { - for i := 0; i < m.retry; i++ { - sentTxn, err := m.m.Make(args...) - if err != nil { - return nil, xerrors.Errorf("failed to Make: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), m.t) - defer cancel() - - events := m.n.GetOrdering().Watch(ctx) - - err = m.n.GetPool().Add(sentTxn) - if err != nil { - return nil, xerrors.Errorf("failed to Add: %v", err) - } - - sentTxnID := sentTxn.GetID() - - accepted := isAccepted(events, sentTxnID) - if accepted { - return sentTxnID, nil - } - - err = m.m.Sync() - if err != nil { - return nil, xerrors.Errorf("failed to sync: %v", err) - } - - cancel() - } - - return nil, xerrors.Errorf("transaction not included after timeout: %v", args) -} - -// isAccepted returns true if the transaction was included then accepted -func isAccepted(events <-chan ordering.Event, txID []byte) bool { - for event := range events { - for _, result := range event.Transactions { - fetchedTxnID := result.GetTransaction().GetID() - - if bytes.Equal(txID, fetchedTxnID) { - accepted, _ := event.Transactions[0].GetStatus() - - return accepted - } - } - } - - return false -} - -func grantAccess(m txManager, signer crypto.Signer) error { - pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to GetPublicKey: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, - {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, - {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, - {Key: "access:grant_command", Value: []byte("all")}, - {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, - {Key: "access:command", Value: []byte("GRANT")}, - } - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf("failed to grantAccess: %v", err) - } - - return nil -} - func createForm(m txManager, title string, admin string) ([]byte, error) { // Define the configuration : configuration := fake.BasicConfiguration @@ -382,49 +280,6 @@ func closeForm(m txManager, formID []byte, admin string) error { return nil } -func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { - var actor dkg.Actor - var err error - - for _, node := range nodes { - d := node.(dVotingNode).GetDkg() - - // put Listen in a goroutine to optimize for speed - actor, err = d.Listen(formID, m) - if err != nil { - return nil, xerrors.Errorf("failed to GetDkg: %v", err) - } - } - - _, err = actor.Setup() - if err != nil { - return nil, xerrors.Errorf("failed to Setup: %v", err) - } - - return actor, nil -} - -func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { - var sActor shuffle.Actor - - for _, node := range nodes { - client := client{ - srvc: node.GetOrdering(), - mgr: node.GetValidationSrv(), - } - - var err error - shuffler := node.GetShuffle() - - sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) - if err != nil { - return nil, xerrors.Errorf("failed to init Shuffle: %v", err) - } - } - - return sActor, nil -} - func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { if form.Status != types.PubSharesSubmitted { return xerrors.Errorf("cannot decrypt: not all pubShares submitted") @@ -452,73 +307,3 @@ func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { return nil } - -func closeNodes(nodes []dVotingCosiDela) error { - wait := sync.WaitGroup{} - wait.Add(len(nodes)) - - for _, n := range nodes { - go func(node dVotingNode) { - defer wait.Done() - node.GetOrdering().Close() - }(n.(dVotingNode)) - } - - done := make(chan struct{}) - - go func() { - wait.Wait() - close(done) - }() - - select { - case <-done: - return nil - case <-time.After(time.Second * 30): - return xerrors.New("failed to close: timeout") - } -} - -func encodeID(ID string) types.ID { - return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) -} - -// waitForStatus polls the nodes until they all updated to the expected status -// for the given form. An error is raised if the timeout expires. -func waitForStatus(status types.Status, formFac types.FormFactory, - formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { - - expiration := time.Now().Add(timeOut) - - isOK := func() (bool, error) { - for _, node := range nodes { - form, err := getForm(formFac, formID, node.GetOrdering()) - if err != nil { - return false, xerrors.Errorf("failed to get form: %v", err) - } - - if form.Status != status { - return false, nil - } - } - - return true, nil - } - - for { - if time.Now().After(expiration) { - return xerrors.New("status check expired") - } - - ok, err := isOK() - if err != nil { - return xerrors.Errorf("failed to check status: %v", err) - } - - if ok { - return nil - } - - time.Sleep(time.Millisecond * 100) - } -} diff --git a/integration/nodes_utility_functions.go b/integration/nodes_utility_functions.go new file mode 100644 index 000000000..b1b62313d --- /dev/null +++ b/integration/nodes_utility_functions.go @@ -0,0 +1,76 @@ +package integration + +import ( + "sync" + "time" + + "github.com/dedis/d-voting/contracts/evoting/types" + "golang.org/x/xerrors" +) + +func closeNodes(nodes []dVotingCosiDela) error { + wait := sync.WaitGroup{} + wait.Add(len(nodes)) + + for _, n := range nodes { + go func(node dVotingNode) { + defer wait.Done() + node.GetOrdering().Close() + }(n.(dVotingNode)) + } + + done := make(chan struct{}) + + go func() { + wait.Wait() + close(done) + }() + + select { + case <-done: + return nil + case <-time.After(time.Second * 30): + return xerrors.New("failed to close: timeout") + } +} + + +// waitForStatus polls the nodes until they all updated to the expected status +// for the given form. An error is raised if the timeout expires. +func waitForStatus(status types.Status, formFac types.FormFactory, + formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { + + expiration := time.Now().Add(timeOut) + + isOK := func() (bool, error) { + for _, node := range nodes { + form, err := getForm(formFac, formID, node.GetOrdering()) + if err != nil { + return false, xerrors.Errorf("failed to get form: %v", err) + } + + if form.Status != status { + return false, nil + } + } + + return true, nil + } + + for { + if time.Now().After(expiration) { + return xerrors.New("status check expired") + } + + ok, err := isOK() + if err != nil { + return xerrors.Errorf("failed to check status: %v", err) + } + + if ok { + return nil + } + + time.Sleep(time.Millisecond * 100) + } +} diff --git a/integration/shuffle_utility_functions.go b/integration/shuffle_utility_functions.go new file mode 100644 index 000000000..956dc1505 --- /dev/null +++ b/integration/shuffle_utility_functions.go @@ -0,0 +1,28 @@ +package integration + +import ( + "github.com/dedis/d-voting/services/shuffle" + "go.dedis.ch/dela/core/txn/signed" + "golang.org/x/xerrors" +) + +func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { + var sActor shuffle.Actor + + for _, node := range nodes { + client := client{ + srvc: node.GetOrdering(), + mgr: node.GetValidationSrv(), + } + + var err error + shuffler := node.GetShuffle() + + sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) + if err != nil { + return nil, xerrors.Errorf("failed to init Shuffle: %v", err) + } + } + + return sActor, nil +} diff --git a/integration/transaction_utility_functions.go b/integration/transaction_utility_functions.go new file mode 100644 index 000000000..12a0aa3bb --- /dev/null +++ b/integration/transaction_utility_functions.go @@ -0,0 +1,115 @@ +package integration + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/hex" + "time" + + "go.dedis.ch/dela/core/execution/native" + "go.dedis.ch/dela/core/ordering" + "go.dedis.ch/dela/core/txn" + "go.dedis.ch/dela/core/txn/signed" + "go.dedis.ch/dela/crypto" + "golang.org/x/xerrors" +) + +const addAndWaitErr = "failed to addAndWait: %v" + +func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, + timeout time.Duration, retry int) txManager { + + client := client{ + srvc: firstNode.GetOrdering(), + mgr: firstNode.GetValidationSrv(), + } + + return txManager{ + m: signed.NewManager(signer, client), + n: firstNode, + t: timeout, + retry: retry, + } +} + +type txManager struct { + m txn.Manager + n dVotingCosiDela + t time.Duration + retry int +} + +func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { + for i := 0; i < m.retry; i++ { + sentTxn, err := m.m.Make(args...) + if err != nil { + return nil, xerrors.Errorf("failed to Make: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), m.t) + defer cancel() + + events := m.n.GetOrdering().Watch(ctx) + + err = m.n.GetPool().Add(sentTxn) + if err != nil { + return nil, xerrors.Errorf("failed to Add: %v", err) + } + + sentTxnID := sentTxn.GetID() + + accepted := isAccepted(events, sentTxnID) + if accepted { + return sentTxnID, nil + } + + err = m.m.Sync() + if err != nil { + return nil, xerrors.Errorf("failed to sync: %v", err) + } + + cancel() + } + + return nil, xerrors.Errorf("transaction not included after timeout: %v", args) +} + +// isAccepted returns true if the transaction was included then accepted +func isAccepted(events <-chan ordering.Event, txID []byte) bool { + for event := range events { + for _, result := range event.Transactions { + fetchedTxnID := result.GetTransaction().GetID() + + if bytes.Equal(txID, fetchedTxnID) { + accepted, _ := event.Transactions[0].GetStatus() + + return accepted + } + } + } + + return false +} + +func grantAccess(m txManager, signer crypto.Signer) error { + pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() + if err != nil { + return xerrors.Errorf("failed to GetPublicKey: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, + {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, + {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, + {Key: "access:grant_command", Value: []byte("all")}, + {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, + {Key: "access:command", Value: []byte("GRANT")}, + } + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf("failed to grantAccess: %v", err) + } + + return nil +} From e57754f0cd3baccb1dc427acbdbbf25b8c6c75cb Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:53:08 +0100 Subject: [PATCH 38/55] Refactor gitHub actions --- .github/workflows/go_bad_vote_test.yml | 26 +++++++++++++++++++++ .github/workflows/go_node_crashing_test.yml | 26 +++++++++++++++++++++ .github/workflows/go_revote_test.yml | 26 +++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 .github/workflows/go_bad_vote_test.yml create mode 100644 .github/workflows/go_node_crashing_test.yml create mode 100644 .github/workflows/go_revote_test.yml diff --git a/.github/workflows/go_bad_vote_test.yml b/.github/workflows/go_bad_vote_test.yml new file mode 100644 index 000000000..0253c41f6 --- /dev/null +++ b/.github/workflows/go_bad_vote_test.yml @@ -0,0 +1,26 @@ +name: Go Integration Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the bad vote test + run: go test -timeout 10m -run TestBadVote ./integration/... + + \ No newline at end of file diff --git a/.github/workflows/go_node_crashing_test.yml b/.github/workflows/go_node_crashing_test.yml new file mode 100644 index 000000000..78345fa77 --- /dev/null +++ b/.github/workflows/go_node_crashing_test.yml @@ -0,0 +1,26 @@ +name: Go Integration Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the crash test + run: go test -timeout 10m -run TestCrash ./integration/... + + \ No newline at end of file diff --git a/.github/workflows/go_revote_test.yml b/.github/workflows/go_revote_test.yml new file mode 100644 index 000000000..00c9787ab --- /dev/null +++ b/.github/workflows/go_revote_test.yml @@ -0,0 +1,26 @@ +name: Go Integration Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + + test: + name: Tests + runs-on: ubuntu-latest + steps: + - name: Set up Go ^1.17 + uses: actions/setup-go@v2 + with: + go-version: ^1.17 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Run the revote test + run: go test -timeout 10m -run TestRevote ./integration/... + + \ No newline at end of file From c670e1a981bcf94f9dc61212956f5b19420ef5a5 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 5 Dec 2022 14:55:08 +0100 Subject: [PATCH 39/55] renamed calls --- .github/workflows/go_bad_vote_test.yml | 2 +- .github/workflows/go_node_crashing_test.yml | 2 +- .github/workflows/go_revote_test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go_bad_vote_test.yml b/.github/workflows/go_bad_vote_test.yml index 0253c41f6..caafdaa90 100644 --- a/.github/workflows/go_bad_vote_test.yml +++ b/.github/workflows/go_bad_vote_test.yml @@ -1,4 +1,4 @@ -name: Go Integration Test +name: Go Bad Vote Test on: push: diff --git a/.github/workflows/go_node_crashing_test.yml b/.github/workflows/go_node_crashing_test.yml index 78345fa77..733bc83a0 100644 --- a/.github/workflows/go_node_crashing_test.yml +++ b/.github/workflows/go_node_crashing_test.yml @@ -1,4 +1,4 @@ -name: Go Integration Test +name: Go Node Crashing Test on: push: diff --git a/.github/workflows/go_revote_test.yml b/.github/workflows/go_revote_test.yml index 00c9787ab..7a6a2829e 100644 --- a/.github/workflows/go_revote_test.yml +++ b/.github/workflows/go_revote_test.yml @@ -1,4 +1,4 @@ -name: Go Integration Test +name: Go Revote Test on: push: From b1ad76188af02ed88f7ef996ff20a5630308b2b6 Mon Sep 17 00:00:00 2001 From: A Date: Wed, 7 Dec 2022 15:03:22 +0100 Subject: [PATCH 40/55] Renamed some files, reformating Modification of github Actions --- .github/workflows/go_bad_vote_test.yml | 26 -- .github/workflows/go_integration_tests.yml | 24 +- .github/workflows/go_node_crashing_test.yml | 26 -- .github/workflows/go_revote_test.yml | 26 -- integration/dkg_utility_functions.go | 29 -- integration/form_utility_functions.go | 309 ------------------- integration/nodes_utility_functions.go | 76 ----- integration/shuffle_utility_functions.go | 28 -- integration/transaction_utility_functions.go | 115 ------- 9 files changed, 9 insertions(+), 650 deletions(-) delete mode 100644 .github/workflows/go_bad_vote_test.yml delete mode 100644 .github/workflows/go_node_crashing_test.yml delete mode 100644 .github/workflows/go_revote_test.yml delete mode 100644 integration/dkg_utility_functions.go delete mode 100644 integration/form_utility_functions.go delete mode 100644 integration/nodes_utility_functions.go delete mode 100644 integration/shuffle_utility_functions.go delete mode 100644 integration/transaction_utility_functions.go diff --git a/.github/workflows/go_bad_vote_test.yml b/.github/workflows/go_bad_vote_test.yml deleted file mode 100644 index caafdaa90..000000000 --- a/.github/workflows/go_bad_vote_test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Go Bad Vote Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - - test: - name: Tests - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.17 - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run the bad vote test - run: go test -timeout 10m -run TestBadVote ./integration/... - - \ No newline at end of file diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 8bee5e554..9d114c922 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -2,12 +2,11 @@ name: Go Integration Test on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: - integration: name: Test Integration runs-on: ubuntu-latest @@ -16,12 +15,12 @@ jobs: uses: actions/setup-go@v2 with: go-version: ^1.17 - + - name: Check out code into the Go module directory uses: actions/checkout@v2 - + - name: Run the integration test - run: go test -timeout 10m -run TestIntegration ./integration/... + run: go test -timeout 10m -run TestIntegration ./integration/... bad_vote: name: Test bad vote runs-on: ubuntu-latest @@ -30,7 +29,7 @@ jobs: uses: actions/setup-go@v2 with: go-version: ^1.17 - + - name: Check out code into the Go module directory uses: actions/checkout@v2 @@ -44,10 +43,10 @@ jobs: uses: actions/setup-go@v2 with: go-version: ^1.17 - + - name: Check out code into the Go module directory uses: actions/checkout@v2 - + - name: Run the crash test run: go test -timeout 10m -run TestCrash ./integration/... revote: @@ -58,14 +57,9 @@ jobs: uses: actions/setup-go@v2 with: go-version: ^1.17 - + - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Run the revote test run: go test -timeout 10m -run TestRevote ./integration/... - - - - - diff --git a/.github/workflows/go_node_crashing_test.yml b/.github/workflows/go_node_crashing_test.yml deleted file mode 100644 index 733bc83a0..000000000 --- a/.github/workflows/go_node_crashing_test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Go Node Crashing Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - - test: - name: Tests - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.17 - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run the crash test - run: go test -timeout 10m -run TestCrash ./integration/... - - \ No newline at end of file diff --git a/.github/workflows/go_revote_test.yml b/.github/workflows/go_revote_test.yml deleted file mode 100644 index 7a6a2829e..000000000 --- a/.github/workflows/go_revote_test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Go Revote Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - - test: - name: Tests - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.17 - uses: actions/setup-go@v2 - with: - go-version: ^1.17 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run the revote test - run: go test -timeout 10m -run TestRevote ./integration/... - - \ No newline at end of file diff --git a/integration/dkg_utility_functions.go b/integration/dkg_utility_functions.go deleted file mode 100644 index 8a583fa19..000000000 --- a/integration/dkg_utility_functions.go +++ /dev/null @@ -1,29 +0,0 @@ -package integration - -import ( - "github.com/dedis/d-voting/services/dkg" - "go.dedis.ch/dela/core/txn" - "golang.org/x/xerrors" -) - -func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, error) { - var actor dkg.Actor - var err error - - for _, node := range nodes { - d := node.(dVotingNode).GetDkg() - - // put Listen in a goroutine to optimize for speed - actor, err = d.Listen(formID, m) - if err != nil { - return nil, xerrors.Errorf("failed to GetDkg: %v", err) - } - } - - _, err = actor.Setup() - if err != nil { - return nil, xerrors.Errorf("failed to Setup: %v", err) - } - - return actor, nil -} diff --git a/integration/form_utility_functions.go b/integration/form_utility_functions.go deleted file mode 100644 index 87029942a..000000000 --- a/integration/form_utility_functions.go +++ /dev/null @@ -1,309 +0,0 @@ -package integration - -import ( - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "io" - "math/rand" - "strconv" - "strings" - - "github.com/dedis/d-voting/contracts/evoting" - "github.com/dedis/d-voting/contracts/evoting/types" - "github.com/dedis/d-voting/internal/testing/fake" - "github.com/dedis/d-voting/services/dkg" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "go.dedis.ch/kyber/v3" - "golang.org/x/xerrors" -) - -var serdecontext = json.NewContext() - -func encodeID(ID string) types.ID { - return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) -} - -func ballotIsNull(ballot types.Ballot) bool { - return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && - ballot.RankResultIDs == nil && ballot.RankResult == nil && - ballot.TextResultIDs == nil && ballot.TextResult == nil -} - -func createForm(m txManager, title string, admin string) ([]byte, error) { - // Define the configuration : - configuration := fake.BasicConfiguration - - createForm := types.CreateForm{ - Configuration: configuration, - AdminID: admin, - } - - data, err := createForm.Serialize(serdecontext) - if err != nil { - return nil, xerrors.Errorf("failed to serialize: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCreateForm)}, - } - - txID, err := m.addAndWait(args...) - if err != nil { - return nil, xerrors.Errorf(addAndWaitErr, err) - } - - // Calculate formID from - hash := sha256.New() - hash.Write(txID) - formID := hash.Sum(nil) - - return formID, nil -} - -func openForm(m txManager, formID []byte) error { - openForm := &types.OpenForm{ - FormID: hex.EncodeToString(formID), - } - - data, err := openForm.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize open form: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdOpenForm)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - return nil -} - -func getForm(formFac serde.Factory, formID []byte, - service ordering.Service) (types.Form, error) { - - form := types.Form{} - - proof, err := service.GetProof(formID) - if err != nil { - return form, xerrors.Errorf("failed to GetProof: %v", err) - } - - if proof == nil { - return form, xerrors.Errorf("form does not exist: %v", err) - } - - message, err := formFac.Deserialize(serdecontext, proof.GetValue()) - if err != nil { - return form, xerrors.Errorf("failed to deserialize Form: %v", err) - } - - form, ok := message.(types.Form) - if !ok { - return form, xerrors.Errorf("wrong message type: %T", message) - } - - return form, nil -} - -func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, - numberOfVotes int) ([]types.Ballot, error) { - - possibleBallots := []string{ - string("select:" + encodeID("bb") + ":0,0,1,0\n" + - "text:" + encodeID("ee") + ":eWVz\n\n"), //encoding of "yes" - string("select:" + encodeID("bb") + ":1,1,0,0\n" + - "text:" + encodeID("ee") + ":amE=\n\n"), //encoding of "ja - string("select:" + encodeID("bb") + ":0,0,0,1\n" + - "text:" + encodeID("ee") + ":b3Vp\n\n"), //encoding of "oui" - } - - votes := make([]types.Ballot, numberOfVotes) - - for i := 0; i < numberOfVotes; i++ { - randomIndex := rand.Intn(len(possibleBallots)) - vote := possibleBallots[randomIndex] - - ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) - if err != nil { - return nil, xerrors.Errorf("failed to marshallBallot: %v", err) - } - - userID := "user " + strconv.Itoa(i) - - castVote := types.CastVote{ - FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, - } - - data, err := castVote.Serialize(serdecontext) - if err != nil { - return nil, xerrors.Errorf("failed to serialize cast vote: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return nil, xerrors.Errorf(addAndWaitErr, err) - } - - var ballot types.Ballot - err = ballot.Unmarshal(vote, form) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal ballot: %v", err) - } - - votes[i] = ballot - } - - return votes, nil -} - -func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { - - possibleBallots := []string{ - string("select:" + encodeID("bb") + ":1,0,1,1\n" + - "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" - string("select:" + encodeID("bb") + ":1,1,1,1\n" + - "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" - - } - - for i := 0; i < numberOfBadVotes; i++ { - randomIndex := rand.Intn(len(possibleBallots)) - vote := possibleBallots[randomIndex] - - ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) - if err != nil { - return xerrors.Errorf("failed to marshallBallot: %v", err) - } - - userID := "badUser " + strconv.Itoa(i) - - castVote := types.CastVote{ - FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, - } - - data, err := castVote.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize cast vote: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - //votes[i] = ballot - } - - return nil -} - -func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { - - var ballot = make([]types.EGPair, chunks) - - buf := make([]byte, 29) - - for i := 0; i < chunks; i++ { - var K, C kyber.Point - var err error - - n, err := vote.Read(buf) - if err != nil { - return nil, xerrors.Errorf("failed to read: %v", err) - } - - K, C, _, err = actor.Encrypt(buf[:n]) - if err != nil { - return types.Ciphervote{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) - } - - ballot[i] = types.EGPair{ - K: K, - C: C, - } - } - - return ballot, nil -} - -func closeForm(m txManager, formID []byte, admin string) error { - closeForm := &types.CloseForm{ - FormID: hex.EncodeToString(formID), - UserID: admin, - } - - data, err := closeForm.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize open form: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCloseForm)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf("failed to Marshall closeForm: %v", err) - } - - return nil -} - -func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { - if form.Status != types.PubSharesSubmitted { - return xerrors.Errorf("cannot decrypt: not all pubShares submitted") - } - - decryptBallots := types.CombineShares{ - FormID: form.FormID, - } - - data, err := decryptBallots.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize ballots: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCombineShares)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - return nil -} diff --git a/integration/nodes_utility_functions.go b/integration/nodes_utility_functions.go deleted file mode 100644 index b1b62313d..000000000 --- a/integration/nodes_utility_functions.go +++ /dev/null @@ -1,76 +0,0 @@ -package integration - -import ( - "sync" - "time" - - "github.com/dedis/d-voting/contracts/evoting/types" - "golang.org/x/xerrors" -) - -func closeNodes(nodes []dVotingCosiDela) error { - wait := sync.WaitGroup{} - wait.Add(len(nodes)) - - for _, n := range nodes { - go func(node dVotingNode) { - defer wait.Done() - node.GetOrdering().Close() - }(n.(dVotingNode)) - } - - done := make(chan struct{}) - - go func() { - wait.Wait() - close(done) - }() - - select { - case <-done: - return nil - case <-time.After(time.Second * 30): - return xerrors.New("failed to close: timeout") - } -} - - -// waitForStatus polls the nodes until they all updated to the expected status -// for the given form. An error is raised if the timeout expires. -func waitForStatus(status types.Status, formFac types.FormFactory, - formID []byte, nodes []dVotingCosiDela, numNodes int, timeOut time.Duration) error { - - expiration := time.Now().Add(timeOut) - - isOK := func() (bool, error) { - for _, node := range nodes { - form, err := getForm(formFac, formID, node.GetOrdering()) - if err != nil { - return false, xerrors.Errorf("failed to get form: %v", err) - } - - if form.Status != status { - return false, nil - } - } - - return true, nil - } - - for { - if time.Now().After(expiration) { - return xerrors.New("status check expired") - } - - ok, err := isOK() - if err != nil { - return xerrors.Errorf("failed to check status: %v", err) - } - - if ok { - return nil - } - - time.Sleep(time.Millisecond * 100) - } -} diff --git a/integration/shuffle_utility_functions.go b/integration/shuffle_utility_functions.go deleted file mode 100644 index 956dc1505..000000000 --- a/integration/shuffle_utility_functions.go +++ /dev/null @@ -1,28 +0,0 @@ -package integration - -import ( - "github.com/dedis/d-voting/services/shuffle" - "go.dedis.ch/dela/core/txn/signed" - "golang.org/x/xerrors" -) - -func initShuffle(nodes []dVotingCosiDela) (shuffle.Actor, error) { - var sActor shuffle.Actor - - for _, node := range nodes { - client := client{ - srvc: node.GetOrdering(), - mgr: node.GetValidationSrv(), - } - - var err error - shuffler := node.GetShuffle() - - sActor, err = shuffler.Listen(signed.NewManager(node.GetShuffleSigner(), client)) - if err != nil { - return nil, xerrors.Errorf("failed to init Shuffle: %v", err) - } - } - - return sActor, nil -} diff --git a/integration/transaction_utility_functions.go b/integration/transaction_utility_functions.go deleted file mode 100644 index 12a0aa3bb..000000000 --- a/integration/transaction_utility_functions.go +++ /dev/null @@ -1,115 +0,0 @@ -package integration - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/hex" - "time" - - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -const addAndWaitErr = "failed to addAndWait: %v" - -func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, - timeout time.Duration, retry int) txManager { - - client := client{ - srvc: firstNode.GetOrdering(), - mgr: firstNode.GetValidationSrv(), - } - - return txManager{ - m: signed.NewManager(signer, client), - n: firstNode, - t: timeout, - retry: retry, - } -} - -type txManager struct { - m txn.Manager - n dVotingCosiDela - t time.Duration - retry int -} - -func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { - for i := 0; i < m.retry; i++ { - sentTxn, err := m.m.Make(args...) - if err != nil { - return nil, xerrors.Errorf("failed to Make: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), m.t) - defer cancel() - - events := m.n.GetOrdering().Watch(ctx) - - err = m.n.GetPool().Add(sentTxn) - if err != nil { - return nil, xerrors.Errorf("failed to Add: %v", err) - } - - sentTxnID := sentTxn.GetID() - - accepted := isAccepted(events, sentTxnID) - if accepted { - return sentTxnID, nil - } - - err = m.m.Sync() - if err != nil { - return nil, xerrors.Errorf("failed to sync: %v", err) - } - - cancel() - } - - return nil, xerrors.Errorf("transaction not included after timeout: %v", args) -} - -// isAccepted returns true if the transaction was included then accepted -func isAccepted(events <-chan ordering.Event, txID []byte) bool { - for event := range events { - for _, result := range event.Transactions { - fetchedTxnID := result.GetTransaction().GetID() - - if bytes.Equal(txID, fetchedTxnID) { - accepted, _ := event.Transactions[0].GetStatus() - - return accepted - } - } - } - - return false -} - -func grantAccess(m txManager, signer crypto.Signer) error { - pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to GetPublicKey: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte("go.dedis.ch/dela.Access")}, - {Key: "access:grant_id", Value: []byte(hex.EncodeToString(evotingAccessKey[:]))}, - {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Evoting")}, - {Key: "access:grant_command", Value: []byte("all")}, - {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, - {Key: "access:command", Value: []byte("GRANT")}, - } - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf("failed to grantAccess: %v", err) - } - - return nil -} From 781a007302dbb7b805c5dd8e250f65d59008b9e8 Mon Sep 17 00:00:00 2001 From: A Date: Wed, 7 Dec 2022 16:06:40 +0100 Subject: [PATCH 41/55] fix github action --- .github/workflows/go_integration_tests.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 9d114c922..8c5aa9dcf 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -7,6 +7,10 @@ on: branches: [main] jobs: +<<<<<<< HEAD +======= + +>>>>>>> 1cfffaf (fix github action) integration: name: Test Integration runs-on: ubuntu-latest @@ -20,7 +24,11 @@ jobs: uses: actions/checkout@v2 - name: Run the integration test +<<<<<<< HEAD run: go test -timeout 10m -run TestIntegration ./integration/... +======= + run: go test -timeout 10m -run TestIntegration ./integration/... +>>>>>>> 1cfffaf (fix github action) bad_vote: name: Test bad vote runs-on: ubuntu-latest From 7915d4c3694d771535a024b76dab2e7ec7bf0903 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 10:04:06 +0100 Subject: [PATCH 42/55] begin adapting scenario test --- integration/integration_test.go | 4 --- integration/scenario_test.go | 48 +++++++++++++++++++------ integration/transaction.go | 64 ++++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 16 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index 863b5da64..16d411620 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -263,11 +263,7 @@ func getIntegrationTestCrash(numNodes, numVotes, failingNodes int) func(*testing t.Logf("shuffling") err = sActor.Shuffle(formID) -<<<<<<< HEAD // If the number of failing nodes is greater -======= - // If the number of failing nodes is greater ->>>>>>> 9242a4a (fix comment) // than the threshold, the shuffle will fail fmt.Println("threshold: ", numNodes/3) if failingNodes > numNodes/3 { diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 611aaa529..c244baeee 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -103,11 +103,13 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray resp, err := http.Post(proxyArray[0]+"/evoting/forms", contentType, bytes.NewBuffer(signed)) require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - body, err := io.ReadAll(resp.Body) require.NoError(t, err) + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) + + + t.Log("response body:", string(body)) resp.Body.Close() @@ -118,6 +120,10 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray formID := createFormResponse.FormID + ok, err := pollTxnInclusion(proxyArray[1], createFormResponse.Token, t) + require.NoError(t, err) + require.True(t, ok) + t.Logf("ID of the form : " + formID) // ##################################### SETUP DKG ######################### @@ -162,8 +168,9 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray randomproxy := "http://localhost:9081" t.Logf("Open form send to proxy %v", randomproxy) - _, err = updateForm(secret, randomproxy, formID, "open", t) + ok, err = updateForm(secret, randomproxy, formID, "open", t) require.NoError(t, err) + require.True(t, ok) // ##################################### GET FORM INFO ################# proxyAddr1 := proxyArray[0] @@ -187,6 +194,7 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray require.NoError(t, err) // ##################################### CAST BALLOTS ###################### + t.Log("cast ballots") //make List of ballots @@ -240,8 +248,16 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray require.NoError(t, err) require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s", resp.Status) - _, err = io.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) + require.NoError(t, err) + + var infos ptypes.TransactionInfoToSend + err = json.Unmarshal(body, &infos) + require.NoError(t, err) + + ok, err = pollTxnInclusion(randomproxy, infos.Token, t) require.NoError(t, err) + require.True(t, ok) resp.Body.Close() @@ -269,8 +285,9 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray t.Logf("Close form (for real) send to proxy %v", randomproxy) - _, err = updateForm(secret, randomproxy, formID, "close", t) + ok, err = updateForm(secret, randomproxy, formID, "close", t) require.NoError(t, err) + require.True(t, ok) time.Sleep(time.Second * 3) @@ -363,8 +380,9 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray randomproxy = proxyArray[rand.Intn(len(proxyArray))] oldTime = time.Now() - _, err = updateForm(secret, randomproxy, formID, "combineShares", t) + ok, err = updateForm(secret, randomproxy, formID, "combineShares", t) require.NoError(t, err) + require.True(t, ok) currentTime = time.Now() diff = currentTime.Sub(oldTime) @@ -528,7 +546,7 @@ func initDKG(secret kyber.Scalar, proxyAddr, formIDHex string, t *testing.T) err return nil } -func updateForm(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (int, error) { +func updateForm(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (bool, error) { msg := ptypes.UpdateFormRequest{ Action: action, } @@ -538,20 +556,28 @@ func updateForm(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *tes req, err := http.NewRequest(http.MethodPut, proxyAddr+"/evoting/forms/"+formIDHex, bytes.NewBuffer(signed)) if err != nil { - return 0, xerrors.Errorf("failed to create request: %v", err) + return false, xerrors.Errorf("failed to create request: %v", err) } resp, err := http.DefaultClient.Do(req) if err != nil { - return 0, xerrors.Errorf("failed retrieve the decryption from the server: %v", err) + return false, xerrors.Errorf("failed retrieve the decryption from the server: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { - return 0, xerrors.Errorf("failed to read response body: %v", err) + return false, xerrors.Errorf("failed to read response body: %v", err) } require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) - return 0, nil + //use the pollTxnInclusion func + var result map[string]interface{} + err = json.Unmarshal(body, &result) + if err != nil { + return false, xerrors.Errorf("failed to unmarshal response body: %v", err) + } + + return pollTxnInclusion(proxyAddr, result["Token"].(string), t) + } func updateDKG(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (int, error) { diff --git a/integration/transaction.go b/integration/transaction.go index 12a0aa3bb..231e9f163 100644 --- a/integration/transaction.go +++ b/integration/transaction.go @@ -5,17 +5,27 @@ import ( "context" "encoding/base64" "encoding/hex" + "encoding/json" + "io" + "net/http" + "testing" "time" + "github.com/stretchr/testify/require" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/signed" "go.dedis.ch/dela/crypto" + ptypes "github.com/dedis/d-voting/proxy/types" "golang.org/x/xerrors" ) -const addAndWaitErr = "failed to addAndWait: %v" +const ( + addAndWaitErr = "failed to addAndWait: %v" + maxPollCount = 20 + interPollWait = 100 * time.Millisecond +) func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, timeout time.Duration, retry int) txManager { @@ -40,6 +50,58 @@ type txManager struct { retry int } +// For scenarioTest +func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { + + for i := 0; i < maxPollCount; i++ { + timeBegin := time.Now() + + req, err := http.NewRequest(http.MethodGet, proxyAddr+"/evoting/transactions/"+token, bytes.NewBuffer([]byte(""))) + if err != nil { + return false, xerrors.Errorf("failed to create request: %v", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false, xerrors.Errorf("failed retrieve the decryption from the server: %v", err) + } + + + body, err := io.ReadAll(resp.Body) + if err != nil { + return false, xerrors.Errorf("failed to read response body: %v", err) + } + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) + + //get the body of the response as json + var result ptypes.TransactionInfoToSend + err = json.Unmarshal(body, &result) + if err != nil { + return false, xerrors.Errorf("failed to unmarshal response body: %v", err) + } + + //check if the transaction is included in the blockchain + + switch result.Status { + case 2: + return false, nil + case 1: + return true, nil + case 0: + token = result.Token + } + + if (time.Now().Sub(timeBegin) < interPollWait) { + time.Sleep(interPollWait - time.Now().Sub(timeBegin)) + } + + } + + return false, xerrors.Errorf("transaction not included after timeout") +} + + +// For integrationTest func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { for i := 0; i < m.retry; i++ { sentTxn, err := m.m.Make(args...) From 8497604661ca4d8314112fd73447d055d008e172 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 10:04:55 +0100 Subject: [PATCH 43/55] integration test --- .github/workflows/go_integration_tests.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/go_integration_tests.yml b/.github/workflows/go_integration_tests.yml index 8c5aa9dcf..e7a24acbb 100644 --- a/.github/workflows/go_integration_tests.yml +++ b/.github/workflows/go_integration_tests.yml @@ -7,10 +7,6 @@ on: branches: [main] jobs: -<<<<<<< HEAD -======= - ->>>>>>> 1cfffaf (fix github action) integration: name: Test Integration runs-on: ubuntu-latest @@ -24,11 +20,7 @@ jobs: uses: actions/checkout@v2 - name: Run the integration test -<<<<<<< HEAD - run: go test -timeout 10m -run TestIntegration ./integration/... -======= run: go test -timeout 10m -run TestIntegration ./integration/... ->>>>>>> 1cfffaf (fix github action) bad_vote: name: Test bad vote runs-on: ubuntu-latest From ade48171e5871f465d7843dc2f5a3884aec0aedb Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 10:29:50 +0100 Subject: [PATCH 44/55] scenario test should work --- integration/integration_test.go | 3 --- integration/scenario_test.go | 11 +++++++++-- integration/transaction.go | 4 +++- proxy/transaction.go | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index 16d411620..d324885c4 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -156,9 +156,6 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { //remove the casted vote from the list castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) ok = true - //remove the casted vote from the list - castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) - break } } require.True(t, ok) diff --git a/integration/scenario_test.go b/integration/scenario_test.go index c244baeee..671e55722 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -33,7 +33,7 @@ import ( var suite = suites.MustFind("Ed25519") -const defaultNodes = 3 +const defaultNodes = 5 // Check the shuffled votes versus the cast votes on a few nodes func TestScenario(t *testing.T) { @@ -120,7 +120,7 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray formID := createFormResponse.FormID - ok, err := pollTxnInclusion(proxyArray[1], createFormResponse.Token, t) + ok, err := pollTxnInclusion(proxyArray[0], createFormResponse.Token, t) require.NoError(t, err) require.True(t, ok) @@ -148,11 +148,13 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray req, err := http.NewRequest(http.MethodPut, proxyArray[0]+"/evoting/services/dkg/actors/"+formID, bytes.NewBuffer(signed)) require.NoError(t, err) + t.Log("Sending") resp, err = http.DefaultClient.Do(req) require.NoError(t, err) require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) // ##################################### OPEN FORM ##################### + t.Log("Open form") // Wait for DKG setup timeTable := make([]float64, 5) oldTime := time.Now() @@ -168,6 +170,7 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray randomproxy := "http://localhost:9081" t.Logf("Open form send to proxy %v", randomproxy) + t.Log("Open form") ok, err = updateForm(secret, randomproxy, formID, "open", t) require.NoError(t, err) require.True(t, ok) @@ -671,10 +674,14 @@ func waitForDKG(proxyAddr, formID string, timeOut time.Duration, t *testing.T) e isOK := func() bool { infoDKG := getDKGInfo(proxyAddr, formID, t) + t.Logf("DKG info: %+v", infoDKG) + + return infoDKG.Status == 1 } for !isOK() { + if time.Now().After(expired) { return xerrors.New("expired") } diff --git a/integration/transaction.go b/integration/transaction.go index 231e9f163..2698073cd 100644 --- a/integration/transaction.go +++ b/integration/transaction.go @@ -52,8 +52,9 @@ type txManager struct { // For scenarioTest func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { - + for i := 0; i < maxPollCount; i++ { + t.Logf("Polling for transaction inclusion: %d/%d", i+1, maxPollCount) timeBegin := time.Now() req, err := http.NewRequest(http.MethodGet, proxyAddr+"/evoting/transactions/"+token, bytes.NewBuffer([]byte(""))) @@ -86,6 +87,7 @@ func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { case 2: return false, nil case 1: + t.Log("Transaction included in the blockchain") return true, nil case 0: token = result.Token diff --git a/proxy/transaction.go b/proxy/transaction.go index b97938bc0..b1d8cde1c 100644 --- a/proxy/transaction.go +++ b/proxy/transaction.go @@ -71,7 +71,7 @@ func (h *form) IsTxnIncluded(w http.ResponseWriter, r *http.Request) { // check if the signature is valid if !h.checkSignature(content.Hash, signature) { - http.Error(w, "invalid signature", http.StatusInternalServerError) + http.Error(w, "invalid signaturee", http.StatusInternalServerError) return } From 8b7a126586dada8d404b665890d145a6acd74ca4 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 10:32:37 +0100 Subject: [PATCH 45/55] integration test fix --- integration/integration_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integration/integration_test.go b/integration/integration_test.go index d324885c4..95db01e83 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -156,6 +156,7 @@ func getIntegrationTest(numNodes, numVotes int) func(*testing.T) { //remove the casted vote from the list castedVotes = append(castedVotes[:i], castedVotes[i+1:]...) ok = true + break } } require.True(t, ok) From 8eb18d429d8f8ad88ee37a57416289ddc0bed748 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 10:37:34 +0100 Subject: [PATCH 46/55] change scenario test to 5 nodes --- .github/workflows/go_scenario_test.yml | 4 ++-- integration/transaction.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go_scenario_test.yml b/.github/workflows/go_scenario_test.yml index 0de07a799..927cc576b 100644 --- a/.github/workflows/go_scenario_test.yml +++ b/.github/workflows/go_scenario_test.yml @@ -32,8 +32,8 @@ jobs: - name: Install memcoin run: make build - - name: Start and setup 3 nodes - run: ./runSystems.sh -n 3 --docker false --backend false --frontend false --attach false + - name: Start and setup 5 nodes + run: ./runSystems.sh -n 5 --docker false --backend false --frontend false --attach false - name: Run the scenario Test run: go test -timeout 7m -run TestScenario ./integration/... diff --git a/integration/transaction.go b/integration/transaction.go index 2698073cd..7c27ee592 100644 --- a/integration/transaction.go +++ b/integration/transaction.go @@ -93,8 +93,8 @@ func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { token = result.Token } - if (time.Now().Sub(timeBegin) < interPollWait) { - time.Sleep(interPollWait - time.Now().Sub(timeBegin)) + if (time.Since(timeBegin) < interPollWait) { + time.Sleep(interPollWait - time.Since(timeBegin)) } } From 1826f6701908921fa72d0e9c95c1bc196f282898 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 10:47:36 +0100 Subject: [PATCH 47/55] reformat --- .github/workflows/go_scenario_test.yml | 9 ++++----- integration/scenario_test.go | 5 +---- proxy/election.go | 1 - 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/go_scenario_test.yml b/.github/workflows/go_scenario_test.yml index 927cc576b..dffbe9b22 100644 --- a/.github/workflows/go_scenario_test.yml +++ b/.github/workflows/go_scenario_test.yml @@ -2,12 +2,11 @@ name: Go Scenario Test (with proxy) on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: - test: name: Tests runs-on: ubuntu-latest @@ -22,10 +21,10 @@ jobs: git clone https://github.com/dedis/dela.git cd dela go install ./cli/crypto - + - name: Check out code into the Go module directory uses: actions/checkout@v2 - + - name: Create a private key run: crypto bls signer new --save private.key diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 671e55722..3a43b2726 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -108,8 +108,6 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) - - t.Log("response body:", string(body)) resp.Body.Close() @@ -675,13 +673,12 @@ func waitForDKG(proxyAddr, formID string, timeOut time.Duration, t *testing.T) e isOK := func() bool { infoDKG := getDKGInfo(proxyAddr, formID, t) t.Logf("DKG info: %+v", infoDKG) - return infoDKG.Status == 1 } for !isOK() { - + if time.Now().After(expired) { return xerrors.New("expired") } diff --git a/proxy/election.go b/proxy/election.go index 441197512..25f01e473 100644 --- a/proxy/election.go +++ b/proxy/election.go @@ -627,4 +627,3 @@ func getForm(ctx serde.Context, formFac serde.Factory, formIDHex string, return form, nil } - From d959f90816b1ffcc174522bf55fbc6d890ee50c5 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 14:13:41 +0100 Subject: [PATCH 48/55] factrorisation in scenario_test --- integration/dkg.go | 123 ++++++++++++ integration/form.go | 125 +++++++++++- integration/nodes.go | 34 ++++ integration/scenario_test.go | 356 +++++++---------------------------- integration/transaction.go | 22 +-- 5 files changed, 362 insertions(+), 298 deletions(-) diff --git a/integration/dkg.go b/integration/dkg.go index 8a583fa19..ac23f2915 100644 --- a/integration/dkg.go +++ b/integration/dkg.go @@ -1,8 +1,18 @@ package integration import ( + "bytes" + "encoding/json" + "net/http" + "strconv" + "testing" + "time" + + ptypes "github.com/dedis/d-voting/proxy/types" "github.com/dedis/d-voting/services/dkg" + "github.com/stretchr/testify/require" "go.dedis.ch/dela/core/txn" + "go.dedis.ch/kyber/v3" "golang.org/x/xerrors" ) @@ -27,3 +37,116 @@ func initDkg(nodes []dVotingCosiDela, formID []byte, m txn.Manager) (dkg.Actor, return actor, nil } + +func startDKGScenario(numNodes int, timeTable []float64, formID string, secret kyber.Scalar, proxyArray []string, t *testing.T) { + t.Log("Init DKG") + + for i := 0; i < numNodes; i++ { + t.Log("Node" + strconv.Itoa(i+1)) + t.Log(proxyArray[i]) + err := initDKG(secret, proxyArray[i], formID, t) + require.NoError(t, err) + + } + t.Log("Setup DKG") + + msg := ptypes.UpdateDKG{ + Action: "setup", + } + signed, err := createSignedRequest(secret, msg) + require.NoError(t, err) + + req, err := http.NewRequest(http.MethodPut, proxyArray[0]+"/evoting/services/dkg/actors/"+formID, bytes.NewBuffer(signed)) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) + + // Wait for DKG setup + err = waitForDKG(proxyArray[0], formID, time.Second*100, t) + require.NoError(t, err) + +} + +// for Scenario +func initDKG(secret kyber.Scalar, proxyAddr, formIDHex string, t *testing.T) error { + setupDKG := ptypes.NewDKGRequest{ + FormID: formIDHex, + } + + signed, err := createSignedRequest(secret, setupDKG) + require.NoError(t, err) + + resp, err := http.Post(proxyAddr+"/evoting/services/dkg/actors", "application/json", bytes.NewBuffer(signed)) + if err != nil { + return xerrors.Errorf("failed to post request: %v", err) + } + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) + + return nil +} + +// for Scenario +func updateDKG(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (int, error) { + msg := ptypes.UpdateDKG{ + Action: action, + } + + signed, err := createSignedRequest(secret, msg) + require.NoError(t, err) + + req, err := http.NewRequest(http.MethodPut, proxyAddr+"/evoting/services/dkg/actors/"+formIDHex, bytes.NewBuffer(signed)) + if err != nil { + return 0, xerrors.Errorf("failed to create request: %v", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return 0, xerrors.Errorf("failed to execute the query: %v", err) + } + + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) + + return 0, nil +} + +// for Scenario +func getDKGInfo(proxyAddr, formID string, t *testing.T) ptypes.GetActorInfo { + + resp, err := http.Get(proxyAddr + "/evoting/services/dkg/actors" + "/" + formID) + require.NoError(t, err) + + var infoDKG ptypes.GetActorInfo + decoder := json.NewDecoder(resp.Body) + + err = decoder.Decode(&infoDKG) + require.NoError(t, err) + + resp.Body.Close() + + return infoDKG + +} + +// for Scenario +func waitForDKG(proxyAddr, formID string, timeOut time.Duration, t *testing.T) error { + expired := time.Now().Add(timeOut) + + isOK := func() bool { + infoDKG := getDKGInfo(proxyAddr, formID, t) + + return infoDKG.Status == 1 + } + + for !isOK() { + + if time.Now().After(expired) { + return xerrors.New("expired") + } + + time.Sleep(time.Millisecond * 1000) + } + + return nil +} diff --git a/integration/form.go b/integration/form.go index 87029942a..41c7e024e 100644 --- a/integration/form.go +++ b/integration/form.go @@ -1,28 +1,35 @@ package integration import ( + "bytes" "crypto/sha256" "encoding/base64" "encoding/hex" + "encoding/json" "io" "math/rand" + "net/http" "strconv" "strings" + "testing" + "time" "github.com/dedis/d-voting/contracts/evoting" "github.com/dedis/d-voting/contracts/evoting/types" "github.com/dedis/d-voting/internal/testing/fake" + ptypes "github.com/dedis/d-voting/proxy/types" "github.com/dedis/d-voting/services/dkg" + "github.com/stretchr/testify/require" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" + jsonDela "go.dedis.ch/dela/serde/json" "go.dedis.ch/kyber/v3" "golang.org/x/xerrors" ) -var serdecontext = json.NewContext() +var serdecontext = jsonDela.NewContext() func encodeID(ID string) types.ID { return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) @@ -34,6 +41,7 @@ func ballotIsNull(ballot types.Ballot) bool { ballot.TextResultIDs == nil && ballot.TextResult == nil } +// for integration tests func createForm(m txManager, title string, admin string) ([]byte, error) { // Define the configuration : configuration := fake.BasicConfiguration @@ -67,6 +75,45 @@ func createForm(m txManager, title string, admin string) ([]byte, error) { return formID, nil } +func createFormScenario(contentType, proxy string, secret kyber.Scalar, t *testing.T) string { + t.Log("Create form") + + configuration := fake.BasicConfiguration + + createSimpleFormRequest := ptypes.CreateFormRequest{ + Configuration: configuration, + AdminID: "adminId", + } + + signed, err := createSignedRequest(secret, createSimpleFormRequest) + require.NoError(t, err) + + resp, err := http.Post(proxy+"/evoting/forms", contentType, bytes.NewBuffer(signed)) + require.NoError(t, err) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) + + t.Log("response body:", string(body)) + resp.Body.Close() + + var createFormResponse ptypes.CreateFormResponse + + err = json.Unmarshal(body, &createFormResponse) + require.NoError(t, err) + + formID := createFormResponse.FormID + + ok, err := pollTxnInclusion(proxy, createFormResponse.Token, t) + require.NoError(t, err) + require.True(t, ok) + + t.Logf("ID of the form : " + formID) + + return formID +} + func openForm(m txManager, formID []byte) error { openForm := &types.OpenForm{ FormID: hex.EncodeToString(formID), @@ -307,3 +354,77 @@ func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { return nil } + +// for Scenario +func waitForFormStatus(proxyAddr, formID string, status uint16, timeOut time.Duration, t *testing.T) error { + expired := time.Now().Add(timeOut) + + isOK := func() bool { + infoForm := getFormInfo(proxyAddr, formID, t) + return infoForm.Status == status + } + + for !isOK() { + if time.Now().After(expired) { + return xerrors.New("expired") + } + + time.Sleep(time.Millisecond * 1000) + } + + return nil +} + +// for Scenario +func updateForm(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (bool, error) { + msg := ptypes.UpdateFormRequest{ + Action: action, + } + + signed, err := createSignedRequest(secret, msg) + require.NoError(t, err) + + req, err := http.NewRequest(http.MethodPut, proxyAddr+"/evoting/forms/"+formIDHex, bytes.NewBuffer(signed)) + if err != nil { + return false, xerrors.Errorf("failed to create request: %v", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false, xerrors.Errorf("failed retrieve the decryption from the server: %v", err) + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return false, xerrors.Errorf("failed to read response body: %v", err) + } + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) + + //use the pollTxnInclusion func + var result map[string]interface{} + err = json.Unmarshal(body, &result) + if err != nil { + return false, xerrors.Errorf("failed to unmarshal response body: %v", err) + } + + return pollTxnInclusion(proxyAddr, result["Token"].(string), t) + +} + +// for Scenario +func getFormInfo(proxyAddr, formID string, t *testing.T) ptypes.GetFormResponse { + // t.Log("Get form info") + + resp, err := http.Get(proxyAddr + "/evoting/forms" + "/" + formID) + require.NoError(t, err) + + var infoForm ptypes.GetFormResponse + decoder := json.NewDecoder(resp.Body) + + err = decoder.Decode(&infoForm) + require.NoError(t, err) + + resp.Body.Close() + + return infoForm + +} diff --git a/integration/nodes.go b/integration/nodes.go index e95190cc7..e389250c2 100644 --- a/integration/nodes.go +++ b/integration/nodes.go @@ -1,10 +1,14 @@ package integration import ( + "fmt" + "os/exec" "sync" + "testing" "time" "github.com/dedis/d-voting/contracts/evoting/types" + "github.com/stretchr/testify/require" "golang.org/x/xerrors" ) @@ -73,3 +77,33 @@ func waitForStatus(status types.Status, formFac types.FormFactory, time.Sleep(time.Millisecond * 100) } } + +// for Scenario +func killNode(proxyArray []string, nodeNub int, t *testing.T) []string { + + proxyArray[nodeNub-1] = proxyArray[len(proxyArray)-1] + proxyArray[len(proxyArray)-1] = "" + proxyArray = proxyArray[:len(proxyArray)-1] + + cmd := exec.Command("docker", "kill", fmt.Sprintf("node%v", nodeNub)) + err := cmd.Run() + require.NoError(t, err) + + return proxyArray +} + +// for Scenario +func restartNode(nodeNub int, t *testing.T) { + cmd := exec.Command("docker", "restart", fmt.Sprintf("node%v", nodeNub)) + err := cmd.Run() + require.NoError(t, err) + + // Replace the relative path + cmd = exec.Command("rm", fmt.Sprintf("/Users/jean-baptistezhang/EPFL_cours/semestre_2/d-voting/nodedata/node%v/daemon.sock", nodeNub)) + err = cmd.Run() + require.NoError(t, err) + + cmd = exec.Command("bash", "-c", fmt.Sprintf("docker exec -d node%v memcoin --config /tmp/node%v start --postinstall --promaddr :9100 --proxyaddr :9080 --proxykey adbacd10fdb9822c71025d6d00092b8a4abb5ebcb673d28d863f7c7c5adaddf3 --listen tcp://0.0.0.0:2001 --public //172.18.0.%v:2001", nodeNub, nodeNub, nodeNub+1)) + err = cmd.Run() + require.NoError(t, err) +} diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 3a43b2726..169832880 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -11,7 +11,6 @@ import ( "math/rand" "net/http" "os" - "os/exec" "reflect" "strconv" "strings" @@ -45,7 +44,7 @@ func TestScenario(t *testing.T) { numNodes, err = strconv.Atoi(n) require.NoError(t, err) } - t.Run("Basic configuration", getScenarioTest(numNodes, numNodes, 1)) + t.Run("Basic configuration", getScenarioTest(numNodes, 20, 1)) } func getScenarioTest(numNodes int, numVotes int, numForm int) func(*testing.T) { @@ -86,92 +85,47 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray secret := suite.Scalar() err = secret.UnmarshalBinary(secretkeyBuf) require.NoError(t, err) + timeTable := make([]float64, 10) - // ###################################### CREATE SIMPLE FORM ###### - - t.Log("Create form") - - configuration := fake.BasicConfiguration - - createSimpleFormRequest := ptypes.CreateFormRequest{ - Configuration: configuration, - AdminID: "adminId", - } - - signed, err := createSignedRequest(secret, createSimpleFormRequest) - require.NoError(t, err) - - resp, err := http.Post(proxyArray[0]+"/evoting/forms", contentType, bytes.NewBuffer(signed)) - require.NoError(t, err) - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) + step := 0 - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) - - t.Log("response body:", string(body)) - resp.Body.Close() - - var createFormResponse ptypes.CreateFormResponse - - err = json.Unmarshal(body, &createFormResponse) - require.NoError(t, err) + // ###################################### CREATE SIMPLE FORM ###### + oldTime := time.Now() - formID := createFormResponse.FormID + formID := createFormScenario(contentType, proxyArray[0], secret, t) - ok, err := pollTxnInclusion(proxyArray[0], createFormResponse.Token, t) - require.NoError(t, err) - require.True(t, ok) + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Creating the election takes: %v sec", timeTable[step]) - t.Logf("ID of the form : " + formID) + step++ // ##################################### SETUP DKG ######################### + oldTime = time.Now() - t.Log("Init DKG") - - for i := 0; i < numNodes; i++ { - t.Log("Node" + strconv.Itoa(i+1)) - t.Log(proxyArray[i]) - err = initDKG(secret, proxyArray[i], formID, t) - require.NoError(t, err) + startDKGScenario(numNodes, timeTable, formID, secret, proxyArray, t) - } - t.Log("Setup DKG") + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("DKG setup takes: %v sec", timeTable[step]) - msg := ptypes.UpdateDKG{ - Action: "setup", - } - signed, err = createSignedRequest(secret, msg) - require.NoError(t, err) - - req, err := http.NewRequest(http.MethodPut, proxyArray[0]+"/evoting/services/dkg/actors/"+formID, bytes.NewBuffer(signed)) - require.NoError(t, err) - - t.Log("Sending") - resp, err = http.DefaultClient.Do(req) - require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) + step++ // ##################################### OPEN FORM ##################### t.Log("Open form") - // Wait for DKG setup - timeTable := make([]float64, 5) - oldTime := time.Now() - - err = waitForDKG(proxyArray[0], formID, time.Second*100, t) - require.NoError(t, err) - - currentTime := time.Now() - diff := currentTime.Sub(oldTime) - timeTable[0] = diff.Seconds() - t.Logf("DKG setup takes: %v sec", diff.Seconds()) randomproxy := "http://localhost:9081" t.Logf("Open form send to proxy %v", randomproxy) - t.Log("Open form") - ok, err = updateForm(secret, randomproxy, formID, "open", t) + oldTime = time.Now() + + ok, err := updateForm(secret, randomproxy, formID, "open", t) require.NoError(t, err) require.True(t, ok) + + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Opening the election takes: %v sec", timeTable[step]) + + step++ + // ##################################### GET FORM INFO ################# proxyAddr1 := proxyArray[0] @@ -198,6 +152,8 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray t.Log("cast ballots") + oldTime = time.Now() + //make List of ballots b1 := string("select:" + encodeIDBallot("bb") + ":0,0,1,0\n" + "text:" + encodeIDBallot("ee") + ":eWVz\n\n") //encoding of "yes" @@ -226,13 +182,10 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray } for i := 0; i < numVotes; i++ { - // t.Logf("ballot in str is: %v", ballotList[i]) ballot, err := marshallBallotManual(ballotList[i], pubKey, Chunksperballot) require.NoError(t, err) - // t.Logf("ballot is: %v", ballot) - castVoteRequest := ptypes.CastVoteRequest{ UserID: "user" + strconv.Itoa(i+1), Ballot: ballot, @@ -241,15 +194,14 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray randomproxy = proxyArray[rand.Intn(len(proxyArray))] t.Logf("cast ballot to proxy %v", randomproxy) - // t.Logf("vote is: %v", castVoteRequest) - signed, err = createSignedRequest(secret, castVoteRequest) + signed, err := createSignedRequest(secret, castVoteRequest) require.NoError(t, err) - resp, err = http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) + resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) require.NoError(t, err) require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s", resp.Status) - body, err = io.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) var infos ptypes.TransactionInfoToSend @@ -263,6 +215,12 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray resp.Body.Close() } + + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Casting %v ballots takes: %v sec", numVotes, timeTable[step]) + + step++ + time.Sleep(time.Second * 5) // Kill and restart the node, change false to true when we want to use @@ -282,6 +240,9 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray } // ############################# CLOSE FORM FOR REAL ################### + + oldTime = time.Now() + randomproxy = proxyArray[rand.Intn(len(proxyArray))] t.Logf("Close form (for real) send to proxy %v", randomproxy) @@ -290,6 +251,11 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray require.NoError(t, err) require.True(t, ok) + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Closing form takes: %v sec", timeTable[step]) + + step++ + time.Sleep(time.Second * 3) getFormResponse = getFormInfo(proxyAddr1, formID, t) @@ -303,33 +269,35 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray t.Log("shuffle ballots") + oldTime = time.Now() + shuffleBallotsRequest := ptypes.UpdateShuffle{ Action: "shuffle", } - signed, err = createSignedRequest(secret, shuffleBallotsRequest) + signed, err := createSignedRequest(secret, shuffleBallotsRequest) require.NoError(t, err) randomproxy = proxyArray[rand.Intn(len(proxyArray))] - req, err = http.NewRequest(http.MethodPut, randomproxy+"/evoting/services/shuffle/"+formID, bytes.NewBuffer(signed)) + req, err := http.NewRequest(http.MethodPut, randomproxy+"/evoting/services/shuffle/"+formID, bytes.NewBuffer(signed)) require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - resp, err = http.DefaultClient.Do(req) + resp, err := http.DefaultClient.Do(req) require.NoError(t, err) + require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[1] = diff.Seconds() - t.Logf("Shuffle takes: %v sec", diff.Seconds()) - - body, err = io.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) require.NoError(t, err) t.Log("Response body: " + string(body)) resp.Body.Close() + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Shuffling ballots takes: %v sec", timeTable[step]) + + step++ + getFormResponse = getFormInfo(proxyAddr1, formID, t) formStatus = getFormResponse.Status @@ -345,31 +313,25 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray t.Log("request public shares") randomproxy = proxyArray[rand.Intn(len(proxyArray))] + oldTime = time.Now() _, err = updateDKG(secret, randomproxy, formID, "computePubshares", t) require.NoError(t, err) - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[2] = diff.Seconds() + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Requesting public shares takes: %v sec", timeTable[step]) - t.Logf("Request public share takes: %v sec", diff.Seconds()) + step++ - time.Sleep(10 * time.Second) + time.Sleep(7 * time.Second) getFormResponse = getFormInfo(proxyAddr1, formID, t) formStatus = getFormResponse.Status - oldTime = time.Now() err = waitForFormStatus(proxyAddr1, formID, uint16(4), time.Second*300, t) require.NoError(t, err) - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[4] = diff.Seconds() - t.Logf("Status goes to 4 takes: %v sec", diff.Seconds()) - t.Logf("Status of the form : %v", formStatus) require.Equal(t, uint16(4), formStatus) @@ -379,17 +341,17 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray t.Log("decrypt ballots") randomproxy = proxyArray[rand.Intn(len(proxyArray))] + oldTime = time.Now() ok, err = updateForm(secret, randomproxy, formID, "combineShares", t) require.NoError(t, err) require.True(t, ok) - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[3] = diff.Seconds() + timeTable[step] = time.Since(oldTime).Seconds() + t.Logf("Decrypting ballots takes: %v sec", timeTable[step]) - t.Logf("decryption takes: %v sec", diff.Seconds()) + step++ time.Sleep(time.Second * 3) @@ -424,11 +386,15 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray } require.True(t, tmpCount, "front end votes are different from decrypted votes") - t.Logf("DKG setup time : %v", timeTable[0]) - t.Logf("shuffle time : %v", timeTable[1]) - t.Logf("Public share time : %v", timeTable[2]) - t.Logf("Status goes to 4 takes: %v sec", diff.Seconds()) - t.Logf("decryption time : %v", timeTable[3]) + t.Logf("Creating the form takes %v sec", timeTable[0]) + t.Logf("Setting up DKG takes %v sec", timeTable[1]) + t.Logf("Oppening the form takes %v sec", timeTable[2]) + t.Logf("Casting %v ballots takes %v sec", numVotes, timeTable[3]) + t.Logf("Closing the form takes %v sec", timeTable[4]) + t.Logf("Shuffling ballots takes %v sec", timeTable[5]) + t.Logf("Requesting public shares takes %v sec", timeTable[6]) + t.Logf("Decrypting ballots takes %v sec", timeTable[7]) + } // ----------------------------------------------------------------------------- @@ -529,181 +495,3 @@ func createSignedRequest(secret kyber.Scalar, msg interface{}) ([]byte, error) { return signedJSON, nil } - -func initDKG(secret kyber.Scalar, proxyAddr, formIDHex string, t *testing.T) error { - setupDKG := ptypes.NewDKGRequest{ - FormID: formIDHex, - } - - signed, err := createSignedRequest(secret, setupDKG) - require.NoError(t, err) - - resp, err := http.Post(proxyAddr+"/evoting/services/dkg/actors", "application/json", bytes.NewBuffer(signed)) - if err != nil { - return xerrors.Errorf("failed to post request: %v", err) - } - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - - return nil -} - -func updateForm(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (bool, error) { - msg := ptypes.UpdateFormRequest{ - Action: action, - } - - signed, err := createSignedRequest(secret, msg) - require.NoError(t, err) - - req, err := http.NewRequest(http.MethodPut, proxyAddr+"/evoting/forms/"+formIDHex, bytes.NewBuffer(signed)) - if err != nil { - return false, xerrors.Errorf("failed to create request: %v", err) - } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return false, xerrors.Errorf("failed retrieve the decryption from the server: %v", err) - } - body, err := io.ReadAll(resp.Body) - if err != nil { - return false, xerrors.Errorf("failed to read response body: %v", err) - } - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", body) - - //use the pollTxnInclusion func - var result map[string]interface{} - err = json.Unmarshal(body, &result) - if err != nil { - return false, xerrors.Errorf("failed to unmarshal response body: %v", err) - } - - return pollTxnInclusion(proxyAddr, result["Token"].(string), t) - -} - -func updateDKG(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *testing.T) (int, error) { - msg := ptypes.UpdateDKG{ - Action: action, - } - - signed, err := createSignedRequest(secret, msg) - require.NoError(t, err) - - req, err := http.NewRequest(http.MethodPut, proxyAddr+"/evoting/services/dkg/actors/"+formIDHex, bytes.NewBuffer(signed)) - if err != nil { - return 0, xerrors.Errorf("failed to create request: %v", err) - } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return 0, xerrors.Errorf("failed to execute the query: %v", err) - } - - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - - return 0, nil -} - -func getFormInfo(proxyAddr, formID string, t *testing.T) ptypes.GetFormResponse { - // t.Log("Get form info") - - resp, err := http.Get(proxyAddr + "/evoting/forms" + "/" + formID) - require.NoError(t, err) - - var infoForm ptypes.GetFormResponse - decoder := json.NewDecoder(resp.Body) - - err = decoder.Decode(&infoForm) - require.NoError(t, err) - - resp.Body.Close() - - return infoForm - -} - -func killNode(proxyArray []string, nodeNub int, t *testing.T) []string { - - proxyArray[nodeNub-1] = proxyArray[len(proxyArray)-1] - proxyArray[len(proxyArray)-1] = "" - proxyArray = proxyArray[:len(proxyArray)-1] - - cmd := exec.Command("docker", "kill", fmt.Sprintf("node%v", nodeNub)) - err := cmd.Run() - require.NoError(t, err) - - return proxyArray -} - -func restartNode(nodeNub int, t *testing.T) { - cmd := exec.Command("docker", "restart", fmt.Sprintf("node%v", nodeNub)) - err := cmd.Run() - require.NoError(t, err) - - // Replace the relative path - cmd = exec.Command("rm", fmt.Sprintf("/Users/jean-baptistezhang/EPFL_cours/semestre_2/d-voting/nodedata/node%v/daemon.sock", nodeNub)) - err = cmd.Run() - require.NoError(t, err) - - cmd = exec.Command("bash", "-c", fmt.Sprintf("docker exec -d node%v memcoin --config /tmp/node%v start --postinstall --promaddr :9100 --proxyaddr :9080 --proxykey adbacd10fdb9822c71025d6d00092b8a4abb5ebcb673d28d863f7c7c5adaddf3 --listen tcp://0.0.0.0:2001 --public //172.18.0.%v:2001", nodeNub, nodeNub, nodeNub+1)) - err = cmd.Run() - require.NoError(t, err) -} - -func getDKGInfo(proxyAddr, formID string, t *testing.T) ptypes.GetActorInfo { - - resp, err := http.Get(proxyAddr + "/evoting/services/dkg/actors" + "/" + formID) - require.NoError(t, err) - - var infoDKG ptypes.GetActorInfo - decoder := json.NewDecoder(resp.Body) - - err = decoder.Decode(&infoDKG) - require.NoError(t, err) - - resp.Body.Close() - - return infoDKG - -} - -func waitForDKG(proxyAddr, formID string, timeOut time.Duration, t *testing.T) error { - expired := time.Now().Add(timeOut) - - isOK := func() bool { - infoDKG := getDKGInfo(proxyAddr, formID, t) - t.Logf("DKG info: %+v", infoDKG) - - return infoDKG.Status == 1 - } - - for !isOK() { - - if time.Now().After(expired) { - return xerrors.New("expired") - } - - time.Sleep(time.Millisecond * 1000) - } - - return nil -} - -func waitForFormStatus(proxyAddr, formID string, status uint16, timeOut time.Duration, t *testing.T) error { - expired := time.Now().Add(timeOut) - - isOK := func() bool { - infoForm := getFormInfo(proxyAddr, formID, t) - return infoForm.Status == status - } - - for !isOK() { - if time.Now().After(expired) { - return xerrors.New("expired") - } - - time.Sleep(time.Millisecond * 1000) - } - - return nil -} diff --git a/integration/transaction.go b/integration/transaction.go index 7c27ee592..e8ee13442 100644 --- a/integration/transaction.go +++ b/integration/transaction.go @@ -11,13 +11,13 @@ import ( "testing" "time" + ptypes "github.com/dedis/d-voting/proxy/types" "github.com/stretchr/testify/require" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/signed" "go.dedis.ch/dela/crypto" - ptypes "github.com/dedis/d-voting/proxy/types" "golang.org/x/xerrors" ) @@ -52,7 +52,7 @@ type txManager struct { // For scenarioTest func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { - + for i := 0; i < maxPollCount; i++ { t.Logf("Polling for transaction inclusion: %d/%d", i+1, maxPollCount) timeBegin := time.Now() @@ -66,7 +66,6 @@ func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { if err != nil { return false, xerrors.Errorf("failed retrieve the decryption from the server: %v", err) } - body, err := io.ReadAll(resp.Body) if err != nil { @@ -84,16 +83,16 @@ func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { //check if the transaction is included in the blockchain switch result.Status { - case 2: - return false, nil - case 1: - t.Log("Transaction included in the blockchain") - return true, nil - case 0: - token = result.Token + case 2: + return false, nil + case 1: + t.Log("Transaction included in the blockchain") + return true, nil + case 0: + token = result.Token } - if (time.Since(timeBegin) < interPollWait) { + if time.Since(timeBegin) < interPollWait { time.Sleep(interPollWait - time.Since(timeBegin)) } @@ -102,7 +101,6 @@ func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { return false, xerrors.Errorf("transaction not included after timeout") } - // For integrationTest func (m txManager) addAndWait(args ...txn.Arg) ([]byte, error) { for i := 0; i < m.retry; i++ { From d1b36ccd40353d5bfb84c8da995bb8a0ddd44e62 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 15:29:32 +0100 Subject: [PATCH 49/55] Refactoring of scenario and load tests --- integration/ballot.go | 433 +++++++++++++++++++++++++++++++++++ integration/form.go | 207 +++-------------- integration/load_test.go | 87 +------ integration/scenario_test.go | 182 +-------------- integration/transaction.go | 4 +- 5 files changed, 476 insertions(+), 437 deletions(-) create mode 100644 integration/ballot.go diff --git a/integration/ballot.go b/integration/ballot.go new file mode 100644 index 000000000..01a514978 --- /dev/null +++ b/integration/ballot.go @@ -0,0 +1,433 @@ +package integration + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "math/rand" + "net/http" + "strconv" + "strings" + "testing" + "time" + + "github.com/dedis/d-voting/contracts/evoting" + "github.com/dedis/d-voting/contracts/evoting/types" + "github.com/dedis/d-voting/internal/testing/fake" + ptypes "github.com/dedis/d-voting/proxy/types" + "github.com/dedis/d-voting/services/dkg" + "github.com/stretchr/testify/require" + "go.dedis.ch/dela/core/execution/native" + "go.dedis.ch/dela/core/txn" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/suites" + "go.dedis.ch/kyber/v3/util/random" + "golang.org/x/xerrors" +) + +var suite = suites.MustFind("Ed25519") + +func ballotIsNull(ballot types.Ballot) bool { + return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && + ballot.RankResultIDs == nil && ballot.RankResult == nil && + ballot.TextResultIDs == nil && ballot.TextResult == nil +} + +func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, + numberOfVotes int) ([]types.Ballot, error) { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":0,0,1,0\n" + + "text:" + encodeID("ee") + ":eWVz\n\n"), //encoding of "yes" + string("select:" + encodeID("bb") + ":1,1,0,0\n" + + "text:" + encodeID("ee") + ":amE=\n\n"), //encoding of "ja + string("select:" + encodeID("bb") + ":0,0,0,1\n" + + "text:" + encodeID("ee") + ":b3Vp\n\n"), //encoding of "oui" + } + + votes := make([]types.Ballot, numberOfVotes) + + for i := 0; i < numberOfVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return nil, xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "user " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return nil, xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return nil, xerrors.Errorf(addAndWaitErr, err) + } + + var ballot types.Ballot + err = ballot.Unmarshal(vote, form) + if err != nil { + return nil, xerrors.Errorf("failed to unmarshal ballot: %v", err) + } + + votes[i] = ballot + } + + return votes, nil +} + +func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { + + possibleBallots := []string{ + string("select:" + encodeID("bb") + ":1,0,1,1\n" + + "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" + string("select:" + encodeID("bb") + ":1,1,1,1\n" + + "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" + + } + + for i := 0; i < numberOfBadVotes; i++ { + randomIndex := rand.Intn(len(possibleBallots)) + vote := possibleBallots[randomIndex] + + ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) + if err != nil { + return xerrors.Errorf("failed to marshallBallot: %v", err) + } + + userID := "badUser " + strconv.Itoa(i) + + castVote := types.CastVote{ + FormID: form.FormID, + UserID: userID, + Ballot: ciphervote, + } + + data, err := castVote.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize cast vote: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + //votes[i] = ballot + } + + return nil +} + +func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { + + var ballot = make([]types.EGPair, chunks) + + buf := make([]byte, 29) + + for i := 0; i < chunks; i++ { + var K, C kyber.Point + var err error + + n, err := vote.Read(buf) + if err != nil { + return nil, xerrors.Errorf("failed to read: %v", err) + } + + K, C, _, err = actor.Encrypt(buf[:n]) + if err != nil { + return types.Ciphervote{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) + } + + ballot[i] = types.EGPair{ + K: K, + C: C, + } + } + + return ballot, nil +} + +func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { + if form.Status != types.PubSharesSubmitted { + return xerrors.Errorf("cannot decrypt: not all pubShares submitted") + } + + decryptBallots := types.CombineShares{ + FormID: form.FormID, + } + + data, err := decryptBallots.Serialize(serdecontext) + if err != nil { + return xerrors.Errorf("failed to serialize ballots: %v", err) + } + + args := []txn.Arg{ + {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, + {Key: evoting.FormArg, Value: data}, + {Key: evoting.CmdArg, Value: []byte(evoting.CmdCombineShares)}, + } + + _, err = m.addAndWait(args...) + if err != nil { + return xerrors.Errorf(addAndWaitErr, err) + } + + return nil +} + +func encryptManual(message []byte, pubkey kyber.Point) (K, C kyber.Point, remainder []byte, err error) { + + // Embed the message (or as much of it as will fit) into a curve point. + M := suite.Point().Embed(message, random.New()) + max := suite.Point().EmbedLen() + if max > len(message) { + max = len(message) + } + remainder = message[max:] + // ElGamal-encrypt the point to produce ciphertext (K,C). + k := suite.Scalar().Pick(random.New()) // ephemeral private key + K = suite.Point().Mul(k, nil) // ephemeral DH public key + S := suite.Point().Mul(k, pubkey) // ephemeral DH shared secret + C = S.Add(S, M) // message blinded with secret + + return K, C, remainder, nil +} + +func chunksPerBallot(size int) int { return (size-1)/29 + 1 } + +func encodeIDBallot(ID string) types.ID { + return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) +} + +func marshallBallotManual(voteStr string, pubkey kyber.Point, chunks int) (ptypes.CiphervoteJSON, error) { + + ballot := make(ptypes.CiphervoteJSON, chunks) + vote := strings.NewReader(voteStr) + fmt.Printf("votestr is: %v", voteStr) + + buf := make([]byte, 29) + + for i := 0; i < chunks; i++ { + var K, C kyber.Point + var err error + + n, err := vote.Read(buf) + if err != nil { + return nil, xerrors.Errorf("failed to read: %v", err) + } + + K, C, _, err = encryptManual(buf[:n], pubkey) + + if err != nil { + return ptypes.CiphervoteJSON{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) + } + + kbuff, err := K.MarshalBinary() + if err != nil { + return ptypes.CiphervoteJSON{}, xerrors.Errorf("failed to marshal K: %v", err) + } + + cbuff, err := C.MarshalBinary() + if err != nil { + return ptypes.CiphervoteJSON{}, xerrors.Errorf("failed to marshal C: %v", err) + } + + ballot[i] = ptypes.EGPairJSON{ + K: kbuff, + C: cbuff, + } + } + + return ballot, nil +} + +func castVotesLoad(numVotesPerSec, numSec int) func(BallotSize, chunksPerBallot int, formID, contentType string, proxyArray []string, pubKey kyber.Point, secret kyber.Scalar, t *testing.T) []types.Ballot { + return (func(BallotSize, chunksPerBallot int, formID, contentType string, proxyArray []string, pubKey kyber.Point, secret kyber.Scalar, t *testing.T) []types.Ballot { + + t.Log("cast ballots") + + //make List of ballots + b1 := string("select:" + encodeIDBallot("bb") + ":0,0,1,0\n" + "text:" + encodeIDBallot("ee") + ":eWVz\n\n") //encoding of "yes" + + numVotes := numVotesPerSec * numSec + + // create all the ballots + ballotList := make([]string, numVotes) + for i := 1; i <= numVotes; i++ { + ballotList[i-1] = b1 + } + + votesfrontend := make([]types.Ballot, numVotes) + + fakeConfiguration := fake.BasicConfiguration + + for i := 0; i < numVotes; i++ { + + var bMarshal types.Ballot + form := types.Form{ + Configuration: fakeConfiguration, + FormID: formID, + BallotSize: BallotSize, + } + + err := bMarshal.Unmarshal(ballotList[i], form) + require.NoError(t, err) + + votesfrontend[i] = bMarshal + } + proxyCount := len(proxyArray) + + // all ballots are identical + ballot, err := marshallBallotManual(b1, pubKey, chunksPerBallot) + require.NoError(t, err) + + // we want to send all the votes and then check if it was included + // in the blockchain + // we will create a list of bools of size numVotes and we will + // set the value to true when we receive the response from the + // blockchain + + inclusionArray := make([]bool, numVotes) + + for i := 0; i < numSec; i++ { + // send the votes asynchrounously and wait for the response + + for j := 0; j < numVotesPerSec; j++ { + randomproxy := proxyArray[rand.Intn(proxyCount)] + castVoteRequest := ptypes.CastVoteRequest{ + UserID: "user" + strconv.Itoa(i*numVotesPerSec+j), + Ballot: ballot, + } + go cast(i*numVotesPerSec+j, castVoteRequest, contentType, randomproxy, formID, secret, inclusionArray, t) + + } + t.Logf("casted votes %d", (i+1)*numVotesPerSec) + time.Sleep(time.Second) + + } + + time.Sleep(time.Second * 30) + + return votesfrontend + }) +} + +func cast(idx int, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, inclusionArray []bool, t *testing.T) { + + t.Logf("cast ballot to proxy %v", randomproxy) + + // t.Logf("vote is: %v", castVoteRequest) + signed, err := createSignedRequest(secret, castVoteRequest) + require.NoError(t, err) + + resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s", resp.Status) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + var infos ptypes.TransactionInfoToSend + err = json.Unmarshal(body, &infos) + require.NoError(t, err) + + ok, err := pollTxnInclusion(randomproxy, infos.Token, t) + require.NoError(t, err) + if ok { + inclusionArray[idx] = true + } + +} + +func castVotesScenario(numVotes int) func(BallotSize, chunksPerBallot int, formID, contentType string, proxyArray []string, pubKey kyber.Point, secret kyber.Scalar, t *testing.T) []types.Ballot { + return (func(BallotSize, chunksPerBallot int, formID, contentType string, proxyArray []string, pubKey kyber.Point, secret kyber.Scalar, t *testing.T) []types.Ballot { + // make List of ballots + b1 := string("select:" + encodeIDBallot("bb") + ":0,0,1,0\n" + "text:" + encodeIDBallot("ee") + ":eWVz\n\n") //encoding of "yes" + + ballotList := make([]string, numVotes) + for i := 1; i <= numVotes; i++ { + ballotList[i-1] = b1 + } + + votesfrontend := make([]types.Ballot, numVotes) + + fakeConfiguration := fake.BasicConfiguration + + for i := 0; i < numVotes; i++ { + + var bMarshal types.Ballot + form := types.Form{ + Configuration: fakeConfiguration, + FormID: formID, + BallotSize: BallotSize, + } + + err := bMarshal.Unmarshal(ballotList[i], form) + require.NoError(t, err) + + votesfrontend[i] = bMarshal + } + + for i := 0; i < numVotes; i++ { + + ballot, err := marshallBallotManual(ballotList[i], pubKey, chunksPerBallot) + require.NoError(t, err) + + castVoteRequest := ptypes.CastVoteRequest{ + UserID: "user" + strconv.Itoa(i+1), + Ballot: ballot, + } + + randomproxy := proxyArray[rand.Intn(len(proxyArray))] + t.Logf("cast ballot to proxy %v", randomproxy) + + signed, err := createSignedRequest(secret, castVoteRequest) + require.NoError(t, err) + + resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s", resp.Status) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + var infos ptypes.TransactionInfoToSend + err = json.Unmarshal(body, &infos) + require.NoError(t, err) + + ok, err := pollTxnInclusion(randomproxy, infos.Token, t) + require.NoError(t, err) + require.True(t, ok) + + resp.Body.Close() + + } + + return votesfrontend + }) +} diff --git a/integration/form.go b/integration/form.go index 41c7e024e..b14c9e1e4 100644 --- a/integration/form.go +++ b/integration/form.go @@ -7,10 +7,7 @@ import ( "encoding/hex" "encoding/json" "io" - "math/rand" "net/http" - "strconv" - "strings" "testing" "time" @@ -18,7 +15,6 @@ import ( "github.com/dedis/d-voting/contracts/evoting/types" "github.com/dedis/d-voting/internal/testing/fake" ptypes "github.com/dedis/d-voting/proxy/types" - "github.com/dedis/d-voting/services/dkg" "github.com/stretchr/testify/require" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering" @@ -26,6 +22,7 @@ import ( "go.dedis.ch/dela/serde" jsonDela "go.dedis.ch/dela/serde/json" "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/sign/schnorr" "golang.org/x/xerrors" ) @@ -35,12 +32,6 @@ func encodeID(ID string) types.ID { return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) } -func ballotIsNull(ballot types.Ballot) bool { - return ballot.SelectResultIDs == nil && ballot.SelectResult == nil && - ballot.RankResultIDs == nil && ballot.RankResult == nil && - ballot.TextResultIDs == nil && ballot.TextResult == nil -} - // for integration tests func createForm(m txManager, title string, admin string) ([]byte, error) { // Define the configuration : @@ -165,143 +156,6 @@ func getForm(formFac serde.Factory, formID []byte, return form, nil } -func castVotesRandomly(m txManager, actor dkg.Actor, form types.Form, - numberOfVotes int) ([]types.Ballot, error) { - - possibleBallots := []string{ - string("select:" + encodeID("bb") + ":0,0,1,0\n" + - "text:" + encodeID("ee") + ":eWVz\n\n"), //encoding of "yes" - string("select:" + encodeID("bb") + ":1,1,0,0\n" + - "text:" + encodeID("ee") + ":amE=\n\n"), //encoding of "ja - string("select:" + encodeID("bb") + ":0,0,0,1\n" + - "text:" + encodeID("ee") + ":b3Vp\n\n"), //encoding of "oui" - } - - votes := make([]types.Ballot, numberOfVotes) - - for i := 0; i < numberOfVotes; i++ { - randomIndex := rand.Intn(len(possibleBallots)) - vote := possibleBallots[randomIndex] - - ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) - if err != nil { - return nil, xerrors.Errorf("failed to marshallBallot: %v", err) - } - - userID := "user " + strconv.Itoa(i) - - castVote := types.CastVote{ - FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, - } - - data, err := castVote.Serialize(serdecontext) - if err != nil { - return nil, xerrors.Errorf("failed to serialize cast vote: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return nil, xerrors.Errorf(addAndWaitErr, err) - } - - var ballot types.Ballot - err = ballot.Unmarshal(vote, form) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal ballot: %v", err) - } - - votes[i] = ballot - } - - return votes, nil -} - -func castBadVote(m txManager, actor dkg.Actor, form types.Form, numberOfBadVotes int) error { - - possibleBallots := []string{ - string("select:" + encodeID("bb") + ":1,0,1,1\n" + - "text:" + encodeID("ee") + ":bm9ub25vbm8=\n\n"), //encoding of "nononono" - string("select:" + encodeID("bb") + ":1,1,1,1\n" + - "text:" + encodeID("ee") + ":bm8=\n\n"), //encoding of "no" - - } - - for i := 0; i < numberOfBadVotes; i++ { - randomIndex := rand.Intn(len(possibleBallots)) - vote := possibleBallots[randomIndex] - - ciphervote, err := marshallBallot(strings.NewReader(vote), actor, form.ChunksPerBallot()) - if err != nil { - return xerrors.Errorf("failed to marshallBallot: %v", err) - } - - userID := "badUser " + strconv.Itoa(i) - - castVote := types.CastVote{ - FormID: form.FormID, - UserID: userID, - Ballot: ciphervote, - } - - data, err := castVote.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize cast vote: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCastVote)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - //votes[i] = ballot - } - - return nil -} - -func marshallBallot(vote io.Reader, actor dkg.Actor, chunks int) (types.Ciphervote, error) { - - var ballot = make([]types.EGPair, chunks) - - buf := make([]byte, 29) - - for i := 0; i < chunks; i++ { - var K, C kyber.Point - var err error - - n, err := vote.Read(buf) - if err != nil { - return nil, xerrors.Errorf("failed to read: %v", err) - } - - K, C, _, err = actor.Encrypt(buf[:n]) - if err != nil { - return types.Ciphervote{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) - } - - ballot[i] = types.EGPair{ - K: K, - C: C, - } - } - - return ballot, nil -} - func closeForm(m txManager, formID []byte, admin string) error { closeForm := &types.CloseForm{ FormID: hex.EncodeToString(formID), @@ -327,34 +181,6 @@ func closeForm(m txManager, formID []byte, admin string) error { return nil } -func decryptBallots(m txManager, actor dkg.Actor, form types.Form) error { - if form.Status != types.PubSharesSubmitted { - return xerrors.Errorf("cannot decrypt: not all pubShares submitted") - } - - decryptBallots := types.CombineShares{ - FormID: form.FormID, - } - - data, err := decryptBallots.Serialize(serdecontext) - if err != nil { - return xerrors.Errorf("failed to serialize ballots: %v", err) - } - - args := []txn.Arg{ - {Key: native.ContractArg, Value: []byte(evoting.ContractName)}, - {Key: evoting.FormArg, Value: data}, - {Key: evoting.CmdArg, Value: []byte(evoting.CmdCombineShares)}, - } - - _, err = m.addAndWait(args...) - if err != nil { - return xerrors.Errorf(addAndWaitErr, err) - } - - return nil -} - // for Scenario func waitForFormStatus(proxyAddr, formID string, status uint16, timeOut time.Duration, t *testing.T) error { expired := time.Now().Add(timeOut) @@ -428,3 +254,34 @@ func getFormInfo(proxyAddr, formID string, t *testing.T) ptypes.GetFormResponse return infoForm } + +func createSignedRequest(secret kyber.Scalar, msg interface{}) ([]byte, error) { + jsonMsg, err := json.Marshal(msg) + if err != nil { + return nil, xerrors.Errorf("failed to marshal json: %v", err) + } + + payload := base64.URLEncoding.EncodeToString(jsonMsg) + + hash := sha256.New() + + hash.Write([]byte(payload)) + md := hash.Sum(nil) + + signature, err := schnorr.Sign(suite, secret, md) + if err != nil { + return nil, xerrors.Errorf("failed to sign: %v", err) + } + + signed := ptypes.SignedRequest{ + Payload: payload, + Signature: hex.EncodeToString(signature), + } + + signedJSON, err := json.Marshal(signed) + if err != nil { + return nil, xerrors.Errorf("failed to create json signed: %v", err) + } + + return signedJSON, nil +} diff --git a/integration/load_test.go b/integration/load_test.go index 0903cabc5..4384fa84d 100644 --- a/integration/load_test.go +++ b/integration/load_test.go @@ -1,5 +1,5 @@ package integration - +/* import ( "bytes" "encoding/hex" @@ -15,11 +15,9 @@ import ( "testing" "time" - "github.com/dedis/d-voting/contracts/evoting/types" "github.com/dedis/d-voting/internal/testing/fake" ptypes "github.com/dedis/d-voting/proxy/types" "github.com/stretchr/testify/require" - "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/util/encoding" ) @@ -328,85 +326,4 @@ func startFormProcessLoad(wg *sync.WaitGroup, numNodes, numVotesPerSec, numSec i t.Logf("decryption time : %v", timeTable[3]) } -func castVotesLoad(numVotesPerSec, numSec, BallotSize, chunksPerBallot int, formID, contentType string, proxyArray []string, pubKey kyber.Point, secret kyber.Scalar, t *testing.T) []types.Ballot { - t.Log("cast ballots") - - //make List of ballots - b1 := string("select:" + encodeIDBallot("bb") + ":0,0,1,0\n" + "text:" + encodeIDBallot("ee") + ":eWVz\n\n") //encoding of "yes" - - numVotes := numVotesPerSec * numSec - - ballotList := make([]string, numVotes) - for i := 1; i <= numVotes; i++ { - ballotList[i-1] = b1 - } - - votesfrontend := make([]types.Ballot, numVotes) - - fakeConfiguration := fake.BasicConfiguration - - for i := 0; i < numVotes; i++ { - - var bMarshal types.Ballot - form := types.Form{ - Configuration: fakeConfiguration, - FormID: formID, - BallotSize: BallotSize, - } - - err := bMarshal.Unmarshal(ballotList[i], form) - require.NoError(t, err) - - votesfrontend[i] = bMarshal - } - proxyCount := len(proxyArray) - - // all ballots are identical - ballot, err := marshallBallotManual(b1, pubKey, chunksPerBallot) - require.NoError(t, err) - - for i := 0; i < numSec; i++ { - // send the votes asynchrounously and wait for the response - - for j := 0; j < numVotesPerSec; j++ { - randomproxy := proxyArray[rand.Intn(proxyCount)] - castVoteRequest := ptypes.CastVoteRequest{ - UserID: "user" + strconv.Itoa(i*numVotesPerSec+j), - Ballot: ballot, - } - go cast(false, castVoteRequest, contentType, randomproxy, formID, secret, t) - - } - t.Logf("casted votes %d", (i+1)*numVotesPerSec) - time.Sleep(time.Second) - - } - - time.Sleep(time.Second * 30) - - return votesfrontend -} - -func cast(isRetry bool, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, t *testing.T) { - - t.Logf("cast ballot to proxy %v", randomproxy) - - // t.Logf("vote is: %v", castVoteRequest) - signed, err := createSignedRequest(secret, castVoteRequest) - require.NoError(t, err) - - resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) - require.NoError(t, err) - - if http.StatusOK != resp.StatusCode && !isRetry { - t.Logf("unexpected status: %s retry", resp.Status) - cast(true, castVoteRequest, contentType, randomproxy, formID, secret, t) - return - } - - responseBody, err := io.ReadAll(resp.Body) - require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s %s", resp.Status, responseBody) - - resp.Body.Close() -} +*/ diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 169832880..81711b729 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -2,10 +2,7 @@ package integration import ( "bytes" - "crypto/sha256" - "encoding/base64" "encoding/hex" - "encoding/json" "fmt" "io" "math/rand" @@ -13,24 +10,17 @@ import ( "os" "reflect" "strconv" - "strings" "sync" "testing" "time" "github.com/dedis/d-voting/contracts/evoting/types" - "github.com/dedis/d-voting/internal/testing/fake" ptypes "github.com/dedis/d-voting/proxy/types" "github.com/stretchr/testify/require" "go.dedis.ch/kyber/v3" - "go.dedis.ch/kyber/v3/sign/schnorr" - "go.dedis.ch/kyber/v3/suites" "go.dedis.ch/kyber/v3/util/encoding" - "go.dedis.ch/kyber/v3/util/random" - "golang.org/x/xerrors" ) -var suite = suites.MustFind("Ed25519") const defaultNodes = 5 @@ -38,6 +28,7 @@ const defaultNodes = 5 func TestScenario(t *testing.T) { var err error numNodes := defaultNodes + t.Log("Start") n, ok := os.LookupEnv("NNODES") if ok { @@ -63,7 +54,7 @@ func getScenarioTest(numNodes int, numVotes int, numForm int) func(*testing.T) { t.Log("Starting worker", i) wg.Add(1) - go startFormProcess(&wg, numNodes, numVotes, proxyList, t, numForm) + go startFormProcess(&wg, numNodes, numVotes, proxyList, t, numForm, castVotesScenario(numVotes)) time.Sleep(2 * time.Second) } @@ -74,7 +65,7 @@ func getScenarioTest(numNodes int, numVotes int, numForm int) func(*testing.T) { } } -func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray []string, t *testing.T, numForm int) { +func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray []string, t *testing.T, numForm int, castFunc func(int,int, string, string, []string, kyber.Point, kyber.Scalar, *testing.T) []types.Ballot) { defer wg.Done() rand.Seed(0) @@ -135,14 +126,14 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray formpubkey := getFormResponse.Pubkey formStatus := getFormResponse.Status BallotSize := getFormResponse.BallotSize - Chunksperballot := chunksPerBallot(BallotSize) + chunksPerBallot := chunksPerBallot(BallotSize) t.Logf("Publickey of the form : " + formpubkey) t.Logf("Status of the form : %v", formStatus) require.NoError(t, err) t.Logf("BallotSize of the form : %v", BallotSize) - t.Logf("Chunksperballot of the form : %v", Chunksperballot) + t.Logf("chunksPerBallot of the form : %v", chunksPerBallot) // Get form public key pubKey, err := encoding.StringHexToPoint(suite, formpubkey) @@ -154,68 +145,8 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray oldTime = time.Now() - //make List of ballots - b1 := string("select:" + encodeIDBallot("bb") + ":0,0,1,0\n" + "text:" + encodeIDBallot("ee") + ":eWVz\n\n") //encoding of "yes" - - ballotList := make([]string, numVotes) - for i := 1; i <= numVotes; i++ { - ballotList[i-1] = b1 - } - - votesfrontend := make([]types.Ballot, numVotes) - - fakeConfiguration := fake.BasicConfiguration - - for i := 0; i < numVotes; i++ { - - var bMarshal types.Ballot - form := types.Form{ - Configuration: fakeConfiguration, - FormID: formID, - BallotSize: BallotSize, - } - - err = bMarshal.Unmarshal(ballotList[i], form) - require.NoError(t, err) - - votesfrontend[i] = bMarshal - } - - for i := 0; i < numVotes; i++ { - - ballot, err := marshallBallotManual(ballotList[i], pubKey, Chunksperballot) - require.NoError(t, err) - - castVoteRequest := ptypes.CastVoteRequest{ - UserID: "user" + strconv.Itoa(i+1), - Ballot: ballot, - } - - randomproxy = proxyArray[rand.Intn(len(proxyArray))] - t.Logf("cast ballot to proxy %v", randomproxy) - - signed, err := createSignedRequest(secret, castVoteRequest) - require.NoError(t, err) - - resp, err := http.Post(randomproxy+"/evoting/forms/"+formID+"/vote", contentType, bytes.NewBuffer(signed)) - require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "unexpected status: %s", resp.Status) - - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - - var infos ptypes.TransactionInfoToSend - err = json.Unmarshal(body, &infos) - require.NoError(t, err) - - ok, err = pollTxnInclusion(randomproxy, infos.Token, t) - require.NoError(t, err) - require.True(t, ok) - - resp.Body.Close() - - } - + votesfrontend := castFunc(BallotSize, chunksPerBallot, formID,contentType, proxyArray, pubKey,secret, t) + timeTable[step] = time.Since(oldTime).Seconds() t.Logf("Casting %v ballots takes: %v sec", numVotes, timeTable[step]) @@ -396,102 +327,3 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray t.Logf("Decrypting ballots takes %v sec", timeTable[7]) } - -// ----------------------------------------------------------------------------- -// Utility functions -func marshallBallotManual(voteStr string, pubkey kyber.Point, chunks int) (ptypes.CiphervoteJSON, error) { - - ballot := make(ptypes.CiphervoteJSON, chunks) - vote := strings.NewReader(voteStr) - fmt.Printf("votestr is: %v", voteStr) - - buf := make([]byte, 29) - - for i := 0; i < chunks; i++ { - var K, C kyber.Point - var err error - - n, err := vote.Read(buf) - if err != nil { - return nil, xerrors.Errorf("failed to read: %v", err) - } - - K, C, _, err = encryptManual(buf[:n], pubkey) - - if err != nil { - return ptypes.CiphervoteJSON{}, xerrors.Errorf("failed to encrypt the plaintext: %v", err) - } - - kbuff, err := K.MarshalBinary() - if err != nil { - return ptypes.CiphervoteJSON{}, xerrors.Errorf("failed to marshal K: %v", err) - } - - cbuff, err := C.MarshalBinary() - if err != nil { - return ptypes.CiphervoteJSON{}, xerrors.Errorf("failed to marshal C: %v", err) - } - - ballot[i] = ptypes.EGPairJSON{ - K: kbuff, - C: cbuff, - } - } - - return ballot, nil -} - -func encryptManual(message []byte, pubkey kyber.Point) (K, C kyber.Point, remainder []byte, err error) { - - // Embed the message (or as much of it as will fit) into a curve point. - M := suite.Point().Embed(message, random.New()) - max := suite.Point().EmbedLen() - if max > len(message) { - max = len(message) - } - remainder = message[max:] - // ElGamal-encrypt the point to produce ciphertext (K,C). - k := suite.Scalar().Pick(random.New()) // ephemeral private key - K = suite.Point().Mul(k, nil) // ephemeral DH public key - S := suite.Point().Mul(k, pubkey) // ephemeral DH shared secret - C = S.Add(S, M) // message blinded with secret - - return K, C, remainder, nil -} - -func chunksPerBallot(size int) int { return (size-1)/29 + 1 } - -func encodeIDBallot(ID string) types.ID { - return types.ID(base64.StdEncoding.EncodeToString([]byte(ID))) -} - -func createSignedRequest(secret kyber.Scalar, msg interface{}) ([]byte, error) { - jsonMsg, err := json.Marshal(msg) - if err != nil { - return nil, xerrors.Errorf("failed to marshal json: %v", err) - } - - payload := base64.URLEncoding.EncodeToString(jsonMsg) - - hash := sha256.New() - - hash.Write([]byte(payload)) - md := hash.Sum(nil) - - signature, err := schnorr.Sign(suite, secret, md) - if err != nil { - return nil, xerrors.Errorf("failed to sign: %v", err) - } - - signed := ptypes.SignedRequest{ - Payload: payload, - Signature: hex.EncodeToString(signature), - } - - signedJSON, err := json.Marshal(signed) - if err != nil { - return nil, xerrors.Errorf("failed to create json signed: %v", err) - } - - return signedJSON, nil -} diff --git a/integration/transaction.go b/integration/transaction.go index e8ee13442..2cedd1330 100644 --- a/integration/transaction.go +++ b/integration/transaction.go @@ -23,8 +23,8 @@ import ( const ( addAndWaitErr = "failed to addAndWait: %v" - maxPollCount = 20 - interPollWait = 100 * time.Millisecond + maxPollCount = 50 + interPollWait = 200 * time.Millisecond ) func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, From f8d77e3d34c5de4833bbf35324e4a94db1eefd6d Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 15:35:06 +0100 Subject: [PATCH 50/55] reformat --- integration/scenario_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 81711b729..f44ba4faa 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -21,7 +21,6 @@ import ( "go.dedis.ch/kyber/v3/util/encoding" ) - const defaultNodes = 5 // Check the shuffled votes versus the cast votes on a few nodes @@ -65,7 +64,7 @@ func getScenarioTest(numNodes int, numVotes int, numForm int) func(*testing.T) { } } -func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray []string, t *testing.T, numForm int, castFunc func(int,int, string, string, []string, kyber.Point, kyber.Scalar, *testing.T) []types.Ballot) { +func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray []string, t *testing.T, numForm int, castFunc func(int, int, string, string, []string, kyber.Point, kyber.Scalar, *testing.T) []types.Ballot) { defer wg.Done() rand.Seed(0) @@ -145,8 +144,8 @@ func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray oldTime = time.Now() - votesfrontend := castFunc(BallotSize, chunksPerBallot, formID,contentType, proxyArray, pubKey,secret, t) - + votesfrontend := castFunc(BallotSize, chunksPerBallot, formID, contentType, proxyArray, pubKey, secret, t) + timeTable[step] = time.Since(oldTime).Seconds() t.Logf("Casting %v ballots takes: %v sec", numVotes, timeTable[step]) From e7510066540229a4e151642761ed0b2393a3a084 Mon Sep 17 00:00:00 2001 From: A Date: Mon, 12 Dec 2022 16:52:10 +0100 Subject: [PATCH 51/55] Load Test updated --- integration/ballot.go | 33 ++-- integration/form.go | 4 +- integration/load_test.go | 287 +---------------------------------- integration/scenario_test.go | 7 + integration/transaction.go | 12 +- 5 files changed, 45 insertions(+), 298 deletions(-) diff --git a/integration/ballot.go b/integration/ballot.go index 01a514978..7e4e63b15 100644 --- a/integration/ballot.go +++ b/integration/ballot.go @@ -25,6 +25,7 @@ import ( "go.dedis.ch/kyber/v3/suites" "go.dedis.ch/kyber/v3/util/random" "golang.org/x/xerrors" + "sync/atomic" ) var suite = suites.MustFind("Ed25519") @@ -308,11 +309,8 @@ func castVotesLoad(numVotesPerSec, numSec int) func(BallotSize, chunksPerBallot // we want to send all the votes and then check if it was included // in the blockchain - // we will create a list of bools of size numVotes and we will - // set the value to true when we receive the response from the - // blockchain - inclusionArray := make([]bool, numVotes) + var includedVoteCount uint64 for i := 0; i < numSec; i++ { // send the votes asynchrounously and wait for the response @@ -323,7 +321,7 @@ func castVotesLoad(numVotesPerSec, numSec int) func(BallotSize, chunksPerBallot UserID: "user" + strconv.Itoa(i*numVotesPerSec+j), Ballot: ballot, } - go cast(i*numVotesPerSec+j, castVoteRequest, contentType, randomproxy, formID, secret, inclusionArray, t) + go cast(i*numVotesPerSec+j, castVoteRequest, contentType, randomproxy, formID, secret, &includedVoteCount, t) } t.Logf("casted votes %d", (i+1)*numVotesPerSec) @@ -331,13 +329,28 @@ func castVotesLoad(numVotesPerSec, numSec int) func(BallotSize, chunksPerBallot } + time.Sleep(time.Second * 20) + + //wait until includedVoteCount == numVotes + for { + if atomic.LoadUint64(&includedVoteCount) == uint64(numVotes) { + break + } + // check every 10 seconds + time.Sleep(time.Second*10) + t.Log("waiting for all votes to be included in the blockchain") + } + + + + time.Sleep(time.Second * 30) return votesfrontend }) } -func cast(idx int, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, inclusionArray []bool, t *testing.T) { +func cast(idx int, castVoteRequest ptypes.CastVoteRequest, contentType, randomproxy, formID string, secret kyber.Scalar, includedVoteCount *uint64, t *testing.T) { t.Logf("cast ballot to proxy %v", randomproxy) @@ -356,10 +369,12 @@ func cast(idx int, castVoteRequest ptypes.CastVoteRequest, contentType, randompr err = json.Unmarshal(body, &infos) require.NoError(t, err) - ok, err := pollTxnInclusion(randomproxy, infos.Token, t) + ok, err := pollTxnInclusion(120,2*time.Second,randomproxy, infos.Token, t) require.NoError(t, err) if ok { - inclusionArray[idx] = true + atomic.AddUint64(includedVoteCount, 1) + t.Logf("vote %d included", idx) + t.Logf("included votes %d", atomic.LoadUint64(includedVoteCount)) } } @@ -420,7 +435,7 @@ func castVotesScenario(numVotes int) func(BallotSize, chunksPerBallot int, formI err = json.Unmarshal(body, &infos) require.NoError(t, err) - ok, err := pollTxnInclusion(randomproxy, infos.Token, t) + ok, err := pollTxnInclusion(60,1*time.Second,randomproxy, infos.Token, t) require.NoError(t, err) require.True(t, ok) diff --git a/integration/form.go b/integration/form.go index b14c9e1e4..75bcc097f 100644 --- a/integration/form.go +++ b/integration/form.go @@ -96,7 +96,7 @@ func createFormScenario(contentType, proxy string, secret kyber.Scalar, t *testi formID := createFormResponse.FormID - ok, err := pollTxnInclusion(proxy, createFormResponse.Token, t) + ok, err := pollTxnInclusion(60, time.Second, proxy, createFormResponse.Token, t) require.NoError(t, err) require.True(t, ok) @@ -232,7 +232,7 @@ func updateForm(secret kyber.Scalar, proxyAddr, formIDHex, action string, t *tes return false, xerrors.Errorf("failed to unmarshal response body: %v", err) } - return pollTxnInclusion(proxyAddr, result["Token"].(string), t) + return pollTxnInclusion(60,time.Second, proxyAddr, result["Token"].(string), t) } diff --git a/integration/load_test.go b/integration/load_test.go index 4384fa84d..7acd3e066 100644 --- a/integration/load_test.go +++ b/integration/load_test.go @@ -1,37 +1,27 @@ package integration -/* + import ( - "bytes" - "encoding/hex" - "encoding/json" "fmt" - "io" - "math/rand" - "net/http" "os" - "reflect" "strconv" "sync" "testing" "time" - "github.com/dedis/d-voting/internal/testing/fake" - ptypes "github.com/dedis/d-voting/proxy/types" "github.com/stretchr/testify/require" - "go.dedis.ch/kyber/v3/util/encoding" ) -// Check the shuffled votes versus the cast votes on a few nodes func TestLoad(t *testing.T) { var err error - numNodes := 10 + numNodes := defaultNodes + t.Log("Start") n, ok := os.LookupEnv("NNODES") if ok { numNodes, err = strconv.Atoi(n) require.NoError(t, err) } - t.Run("Basic configuration", getLoadTest(numNodes, 10, 60, 1)) + t.Run("Basic configuration", getLoadTest(numNodes, 2, 60, 1)) } func getLoadTest(numNodes, numVotesPerSec, numSec, numForm int) func(*testing.T) { @@ -50,7 +40,7 @@ func getLoadTest(numNodes, numVotesPerSec, numSec, numForm int) func(*testing.T) t.Log("Starting worker", i) wg.Add(1) - go startFormProcessLoad(&wg, numNodes, numVotesPerSec, numSec, proxyList, t, numForm) + go startFormProcess(&wg, numNodes, numVotesPerSec*numSec, proxyList, t, numForm, castVotesLoad(numVotesPerSec, numSec)) time.Sleep(2 * time.Second) } @@ -60,270 +50,3 @@ func getLoadTest(numNodes, numVotesPerSec, numSec, numForm int) func(*testing.T) } } - -func startFormProcessLoad(wg *sync.WaitGroup, numNodes, numVotesPerSec, numSec int, proxyArray []string, t *testing.T, numForm int) { - defer wg.Done() - rand.Seed(0) - - const contentType = "application/json" - secretkeyBuf, err := hex.DecodeString("28912721dfd507e198b31602fb67824856eb5a674c021d49fdccbe52f0234409") - require.NoError(t, err) - - secret := suite.Scalar() - err = secret.UnmarshalBinary(secretkeyBuf) - require.NoError(t, err) - - // ###################################### CREATE SIMPLE FORM ###### - - t.Log("Create form") - - configuration := fake.BasicConfiguration - - createSimpleFormRequest := ptypes.CreateFormRequest{ - Configuration: configuration, - AdminID: "adminId", - } - - signed, err := createSignedRequest(secret, createSimpleFormRequest) - require.NoError(t, err) - - resp, err := http.Post(proxyArray[0]+"/evoting/forms", contentType, bytes.NewBuffer(signed)) - require.NoError(t, err) - - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s %s", resp.Status, body) - - t.Log("response body:", string(body)) - resp.Body.Close() - - var createFormResponse ptypes.CreateFormResponse - - err = json.Unmarshal(body, &createFormResponse) - require.NoError(t, err) - - formID := createFormResponse.FormID - - t.Logf("ID of the form : " + formID) - - // ##################################### SETUP DKG ######################### - - t.Log("Init DKG") - - for i := 0; i < numNodes; i++ { - t.Log("Node" + strconv.Itoa(i+1)) - t.Log(proxyArray[i]) - err = initDKG(secret, proxyArray[i], formID, t) - require.NoError(t, err) - - } - t.Log("Setup DKG") - - msg := ptypes.UpdateDKG{ - Action: "setup", - } - signed, err = createSignedRequest(secret, msg) - require.NoError(t, err) - - req, err := http.NewRequest(http.MethodPut, proxyArray[0]+"/evoting/services/dkg/actors/"+formID, bytes.NewBuffer(signed)) - require.NoError(t, err) - - resp, err = http.DefaultClient.Do(req) - require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - - // ##################################### OPEN FORM ##################### - // Wait for DKG setup - timeTable := make([]float64, 5) - oldTime := time.Now() - - err = waitForDKG(proxyArray[0], formID, time.Second*100, t) - require.NoError(t, err) - - currentTime := time.Now() - diff := currentTime.Sub(oldTime) - timeTable[0] = diff.Seconds() - t.Logf("DKG setup takes: %v sec", diff.Seconds()) - - randomproxy := "http://localhost:9081" - t.Logf("Open form send to proxy %v", randomproxy) - - _, err = updateForm(secret, randomproxy, formID, "open", t) - require.NoError(t, err) - // ##################################### GET FORM INFO ################# - - proxyAddr1 := proxyArray[0] - time.Sleep(time.Second * 5) - - getFormResponse := getFormInfo(proxyAddr1, formID, t) - formpubkey := getFormResponse.Pubkey - formStatus := getFormResponse.Status - BallotSize := getFormResponse.BallotSize - Chunksperballot := chunksPerBallot(BallotSize) - - t.Logf("Publickey of the form : " + formpubkey) - t.Logf("Status of the form : %v", formStatus) - - require.NoError(t, err) - t.Logf("BallotSize of the form : %v", BallotSize) - t.Logf("Chunksperballot of the form : %v", Chunksperballot) - - // Get form public key - pubKey, err := encoding.StringHexToPoint(suite, formpubkey) - require.NoError(t, err) - - // ##################################### CAST BALLOTS ###################### - - votesfrontend := castVotesLoad(numVotesPerSec, numSec, BallotSize, Chunksperballot, formID, contentType, proxyArray, pubKey, secret, t) - - // ############################# CLOSE FORM FOR REAL ################### - randomproxy = proxyArray[rand.Intn(len(proxyArray))] - - t.Logf("Close form (for real) send to proxy %v", randomproxy) - - _, err = updateForm(secret, randomproxy, formID, "close", t) - require.NoError(t, err) - - time.Sleep(time.Second * 3) - - getFormResponse = getFormInfo(proxyAddr1, formID, t) - formStatus = getFormResponse.Status - - t.Logf("Status of the form : %v", formStatus) - require.Equal(t, uint16(2), formStatus) - - // ###################################### SHUFFLE BALLOTS ################## - time.Sleep(time.Second * 5) - - t.Log("shuffle ballots") - - shuffleBallotsRequest := ptypes.UpdateShuffle{ - Action: "shuffle", - } - - signed, err = createSignedRequest(secret, shuffleBallotsRequest) - require.NoError(t, err) - - randomproxy = proxyArray[rand.Intn(len(proxyArray))] - - req, err = http.NewRequest(http.MethodPut, randomproxy+"/evoting/services/shuffle/"+formID, bytes.NewBuffer(signed)) - require.NoError(t, err) - require.Equal(t, resp.StatusCode, http.StatusOK, "unexpected status: %s", resp.Status) - - resp, err = http.DefaultClient.Do(req) - require.NoError(t, err) - - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[1] = diff.Seconds() - t.Logf("Shuffle takes: %v sec", diff.Seconds()) - - body, err = io.ReadAll(resp.Body) - require.NoError(t, err) - - t.Log("Response body: " + string(body)) - resp.Body.Close() - - getFormResponse = getFormInfo(proxyAddr1, formID, t) - formStatus = getFormResponse.Status - - err = waitForFormStatus(proxyAddr1, formID, uint16(3), time.Second*100, t) - require.NoError(t, err) - - t.Logf("Status of the form : %v", formStatus) - require.Equal(t, uint16(3), formStatus) - - // ###################################### REQUEST PUBLIC SHARES ############ - time.Sleep(time.Second * 5) - - t.Log("request public shares") - - randomproxy = proxyArray[rand.Intn(len(proxyArray))] - oldTime = time.Now() - - _, err = updateDKG(secret, randomproxy, formID, "computePubshares", t) - require.NoError(t, err) - - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[2] = diff.Seconds() - - t.Logf("Request public share takes: %v sec", diff.Seconds()) - - time.Sleep(10 * time.Second) - - getFormResponse = getFormInfo(proxyAddr1, formID, t) - formStatus = getFormResponse.Status - - oldTime = time.Now() - err = waitForFormStatus(proxyAddr1, formID, uint16(4), time.Second*300, t) - require.NoError(t, err) - - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[4] = diff.Seconds() - t.Logf("Status goes to 4 takes: %v sec", diff.Seconds()) - - t.Logf("Status of the form : %v", formStatus) - require.Equal(t, uint16(4), formStatus) - - // ###################################### DECRYPT BALLOTS ################## - time.Sleep(time.Second * 5) - - t.Log("decrypt ballots") - - randomproxy = proxyArray[rand.Intn(len(proxyArray))] - oldTime = time.Now() - - _, err = updateForm(secret, randomproxy, formID, "combineShares", t) - require.NoError(t, err) - - currentTime = time.Now() - diff = currentTime.Sub(oldTime) - timeTable[3] = diff.Seconds() - - t.Logf("decryption takes: %v sec", diff.Seconds()) - - time.Sleep(time.Second * 7) - - getFormResponse = getFormInfo(proxyAddr1, formID, t) - formStatus = getFormResponse.Status - - err = waitForFormStatus(proxyAddr1, formID, uint16(5), time.Second*100, t) - require.NoError(t, err) - - t.Logf("Status of the form : %v", formStatus) - require.Equal(t, uint16(5), formStatus) - - //#################################### VALIDATE FORM RESULT ############## - - tmpBallots := getFormResponse.Result - var tmpCount bool - require.Equal(t, len(tmpBallots), numVotesPerSec*numSec) - - for _, ballotIntem := range tmpBallots { - tmpComp := ballotIntem - tmpCount = false - for _, voteFront := range votesfrontend { - // t.Logf("voteFront: %v", voteFront) - // t.Logf("tmpComp: %v", tmpComp) - - tmpCount = reflect.DeepEqual(tmpComp, voteFront) - // t.Logf("tmpCount: %v", tmpCount) - - if tmpCount { - break - } - } - } - - require.True(t, tmpCount, "front end votes are different from decrypted votes") - t.Logf("DKG setup time : %v", timeTable[0]) - t.Logf("shuffle time : %v", timeTable[1]) - t.Logf("Public share time : %v", timeTable[2]) - t.Logf("Status goes to 4 takes: %v sec", diff.Seconds()) - t.Logf("decryption time : %v", timeTable[3]) -} - -*/ diff --git a/integration/scenario_test.go b/integration/scenario_test.go index f44ba4faa..eddd2b5a5 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -64,6 +64,13 @@ func getScenarioTest(numNodes int, numVotes int, numForm int) func(*testing.T) { } } + + + + + + + func startFormProcess(wg *sync.WaitGroup, numNodes int, numVotes int, proxyArray []string, t *testing.T, numForm int, castFunc func(int, int, string, string, []string, kyber.Point, kyber.Scalar, *testing.T) []types.Ballot) { defer wg.Done() rand.Seed(0) diff --git a/integration/transaction.go b/integration/transaction.go index 2cedd1330..e8af1ef4f 100644 --- a/integration/transaction.go +++ b/integration/transaction.go @@ -24,7 +24,7 @@ import ( const ( addAndWaitErr = "failed to addAndWait: %v" maxPollCount = 50 - interPollWait = 200 * time.Millisecond + interPollWait = 1 * time.Second ) func newTxManager(signer crypto.Signer, firstNode dVotingCosiDela, @@ -51,10 +51,12 @@ type txManager struct { } // For scenarioTest -func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { - +func pollTxnInclusion(maxPollCount int ,interPollWait time.Duration,proxyAddr, token string, t *testing.T) (bool, error) { + t.Logf("Starting polling for transaction inclusion") for i := 0; i < maxPollCount; i++ { - t.Logf("Polling for transaction inclusion: %d/%d", i+1, maxPollCount) + if i%10 == 0 { + t.Logf("Polling for transaction inclusion: %d/%d", i,maxPollCount) + } timeBegin := time.Now() req, err := http.NewRequest(http.MethodGet, proxyAddr+"/evoting/transactions/"+token, bytes.NewBuffer([]byte(""))) @@ -86,7 +88,7 @@ func pollTxnInclusion(proxyAddr, token string, t *testing.T) (bool, error) { case 2: return false, nil case 1: - t.Log("Transaction included in the blockchain") + t.Logf("Transaction included in the blockchain at iteration: %d/%d", i,maxPollCount) return true, nil case 0: token = result.Token From 10357c5e6256456a6a5c79464e2c7349a75e195f Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 13 Dec 2022 23:25:25 +0100 Subject: [PATCH 52/55] test for shuffle --- proxy/shuffle_test.go | 251 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 proxy/shuffle_test.go diff --git a/proxy/shuffle_test.go b/proxy/shuffle_test.go new file mode 100644 index 000000000..e97435648 --- /dev/null +++ b/proxy/shuffle_test.go @@ -0,0 +1,251 @@ +package proxy + +import ( + "crypto/cipher" + + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "go.dedis.ch/kyber/v3" +) + +/* +func TestEditShuffle(t *testing.T) { + + // create a test server to use the handler + req := httptest.NewRequest("POST", "http://example.com/foo", nil) + w := httptest.NewRecorder() + // create a new shuffle + actor := &shuffleSrv.Shuffle{} + // Create a new Ed25519 key pair + suite := eddsa.NewEdDSA() + baseScalar := suite.Scalar().Zero() + baseSc +} + + +func TestShuffle(t *testing.T) { + actor := &shuffleSrv.ActorMock{} + pk := kyber.NewPoint() + s := NewShuffle(actor, pk) + r := &http.Request{ + Body: &types.SignedRequest{ + Request: types.UpdateShuffle{ + Action: "shuffle", + }, + }, + } + vars := map[string]string{ + "formID": "123456", + } + r = mux.SetURLVars(r, vars) + + w := &responseWriterMock{} + s.EditShuffle(w, r) + + if !actor.CalledShuffle { + t.Error("shuffle not called") + } + if w.ErrorMessage != "" { + t.Errorf("unexpected error message: %s", w.ErrorMessage) + } + + r = &http.Request{ + Body: &types.SignedRequest{ + Request: types + + + +func TestNewShuffle(t *testing.T) { + // Create a mock shuffle actor + mockActor := &shuffleSrv.Actor{} + + // Create a mock public key + mockPk := &kyber.Point{} + + // Call the NewShuffle function + s := NewShuffle(mockActor, mockPk) + + // Assert that the returned value is of the expected type + if _, ok := s.(*shuffle); !ok { + t.Errorf("Expected NewShuffle to return a *shuffle, got %T", s) + } +} + + + +func TestEditShuffle(t *testing.T) { + // Initialize the shuffle struct with a mock Actor and public key + s := shuffle{ + actor: mockActor{}, + pk: mockPk, + } + + // Create a new HTTP request with a formID in the URL + r, err := http.NewRequest("GET", "/edit_shuffle/form123", nil) + if err != nil { + t.Fatal(err) + } + + // Create a new response recorder to record the response + w := httptest.NewRecorder() + + // Call the EditShuffle method with the request and response recorder + s.EditShuffle(w, r) + + // Check the response status code + if w.Code != http.StatusOK { + t.Errorf("expected status OK; got %v", w.Code) + } + + // Check the response body to see if it matches the expected output + expected := "shuffle successful" + if w.Body.String() != expected { + t.Errorf("expected %q; got %q", expected, w.Body.String()) + } +} +*/ +// mockActor is a mock implementation of the shuffleSrv.Actor interface +type mockActor struct{} + +// Shuffle is a mock implementation of the shuffleSrv.Actor.Shuffle method +func (a mockActor) Shuffle(formID []byte) error { + // Return nil to indicate that the shuffle was successful + return nil +} + +type mockPK struct { + // This field can be used to store any data that the mock public key needs to + // maintain state, such as the value of the public key. +} + +func (m mockPK) Verify(message []byte, signature []byte) bool { + // This method simulates the behavior of the mock public key when it is called + // to verify a message and signature. It can return a hard-coded boolean value + // to indicate whether the verification was successful or not. + return false +} + +func (m mockPK) Add(a kyber.Point, b kyber.Point) kyber.Point{ + // This method simulates the behavior of the mock public key when it is called + // to add another point to the public key. It can return a hard-coded value to + // indicate the result of the addition operation. + return nil + +} + +func (m mockPK) Base() kyber.Point { + // This method simulates the behavior of the mock public key when it is called + // to get the base point of the public key. It can return a hard-coded value to + // indicate the base point. + return nil +} +func (m mockPK) Clone() kyber.Point { + // This method simulates the behavior of the mock public key when it is called + // to clone the public key. It can return a hard-coded value to indicate the + // cloned public key. + return nil +} +func (m mockPK) Data() ([]byte, error) { + return nil, nil +} +func (m mockPK) Embed(data []byte, r cipher.Stream) kyber.Point { + return nil + +} + +func (m mockPK) EmbedLen() int { + return 0 +} +func (m mockPK) MarshalBinary() ([]byte, error) { + return nil, nil +} +func (m mockPK) MarshalSize() int { + return 0 +} +func (m mockPK) MarshalTo(w io.Writer) (int, error) { + return 0, nil +} + + +func (m mockPK) Null() kyber.Point { + return nil +} +func (m mockPK) Equal(b kyber.Point) bool { + return false //todo +} +func (m mockPK) Pick(rand cipher.Stream) kyber.Point { + return nil + +} +func (m mockPK) Mul(s kyber.Scalar, b kyber.Point) kyber.Point { + return nil +} +func (m mockPK) Neg(b kyber.Point) kyber.Point { + return nil +} +func (m mockPK) Sub(a kyber.Point, b kyber.Point) kyber.Point { + return nil +} +func (m mockPK) Set(a kyber.Point) kyber.Point { + return nil +} +func (m mockPK) SetInt64(v int64) kyber.Point { + return nil +} +func (m mockPK) String() string { + return "" +} +func (m mockPK) UnmarshalBinary(buff []byte) error { + return nil +} +func (m mockPK) PickLen() int { + return 0 +} +func (m mockPK) PickRand(rand cipher.Stream) kyber.Point { + return nil +} +func (m mockPK) SetBytes(buff []byte) kyber.Point { + return nil +} + +func (m mockPK) UnmarshalFrom(r io.Reader) (int, error) { + return 0, nil +} + + + + +func TestEditShuffle(t *testing.T) { + // Create a new shuffle instance with a mock actor and public key + shuffle := NewShuffle(mockActor{}, mockPK{}) + + // Create a new HTTP request with the "shuffle" action and a valid formID + req, err := http.NewRequest("POST", "/shuffle", strings.NewReader(`{"action": "shuffle", "formID": "123456"}`)) + if err != nil { + t.Fatalf("Error creating request: %v", err) + } + + // Create a new HTTP response recorder to record the response + rr := httptest.NewRecorder() + + // Call the EditShuffle function with the request and response recorder + shuffle.EditShuffle(rr, req) + + // Check the status code of the response + if status := rr.Code; status != http.StatusOK { + t.Errorf("EditShuffle returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Check the response body + expected := `{"success": true}` + if rr.Body.String() != expected { + t.Errorf("EditShuffle returned unexpected body: got %v want %v", + rr.Body.String(), expected) + } + } + From 270d0781140c0831d6316cd90ed3ba7e1272ad85 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 13 Dec 2022 23:31:13 +0100 Subject: [PATCH 53/55] reformat of shuffle_test --- proxy/shuffle_test.go | 148 +++++++++--------------------------------- 1 file changed, 30 insertions(+), 118 deletions(-) diff --git a/proxy/shuffle_test.go b/proxy/shuffle_test.go index e97435648..1aa66c7ca 100644 --- a/proxy/shuffle_test.go +++ b/proxy/shuffle_test.go @@ -12,102 +12,41 @@ import ( "go.dedis.ch/kyber/v3" ) -/* -func TestEditShuffle(t *testing.T) { - // create a test server to use the handler - req := httptest.NewRequest("POST", "http://example.com/foo", nil) - w := httptest.NewRecorder() - // create a new shuffle - actor := &shuffleSrv.Shuffle{} - // Create a new Ed25519 key pair - suite := eddsa.NewEdDSA() - baseScalar := suite.Scalar().Zero() - baseSc -} - - -func TestShuffle(t *testing.T) { - actor := &shuffleSrv.ActorMock{} - pk := kyber.NewPoint() - s := NewShuffle(actor, pk) - r := &http.Request{ - Body: &types.SignedRequest{ - Request: types.UpdateShuffle{ - Action: "shuffle", - }, - }, - } - vars := map[string]string{ - "formID": "123456", - } - r = mux.SetURLVars(r, vars) - w := &responseWriterMock{} - s.EditShuffle(w, r) - - if !actor.CalledShuffle { - t.Error("shuffle not called") +func TestEditShuffle(t *testing.T) { + // Create a new shuffle instance with a mock actor and public key + shuffle := NewShuffle(mockActor{}, mockPK{}) + + // Create a new HTTP request with the "shuffle" action and a valid formID + req, err := http.NewRequest("POST", "/shuffle", strings.NewReader(`{"action": "shuffle", "formID": "123456"}`)) + if err != nil { + t.Fatalf("Error creating request: %v", err) } - if w.ErrorMessage != "" { - t.Errorf("unexpected error message: %s", w.ErrorMessage) + + // Create a new HTTP response recorder to record the response + rr := httptest.NewRecorder() + + // Call the EditShuffle function with the request and response recorder + shuffle.EditShuffle(rr, req) + + // Check the status code of the response + if status := rr.Code; status != http.StatusOK { + t.Errorf("EditShuffle returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Check the response body + expected := `{"success": true}` + if rr.Body.String() != expected { + t.Errorf("EditShuffle returned unexpected body: got %v want %v", + rr.Body.String(), expected) } - - r = &http.Request{ - Body: &types.SignedRequest{ - Request: types - - - -func TestNewShuffle(t *testing.T) { - // Create a mock shuffle actor - mockActor := &shuffleSrv.Actor{} - - // Create a mock public key - mockPk := &kyber.Point{} - - // Call the NewShuffle function - s := NewShuffle(mockActor, mockPk) - - // Assert that the returned value is of the expected type - if _, ok := s.(*shuffle); !ok { - t.Errorf("Expected NewShuffle to return a *shuffle, got %T", s) } -} +//------------------------------------------------------------MOCKS------------------------------------------------------------ +///////////////////////////////////////////mockActor/////////////////////////////////////////// - -func TestEditShuffle(t *testing.T) { - // Initialize the shuffle struct with a mock Actor and public key - s := shuffle{ - actor: mockActor{}, - pk: mockPk, - } - - // Create a new HTTP request with a formID in the URL - r, err := http.NewRequest("GET", "/edit_shuffle/form123", nil) - if err != nil { - t.Fatal(err) - } - - // Create a new response recorder to record the response - w := httptest.NewRecorder() - - // Call the EditShuffle method with the request and response recorder - s.EditShuffle(w, r) - - // Check the response status code - if w.Code != http.StatusOK { - t.Errorf("expected status OK; got %v", w.Code) - } - - // Check the response body to see if it matches the expected output - expected := "shuffle successful" - if w.Body.String() != expected { - t.Errorf("expected %q; got %q", expected, w.Body.String()) - } -} -*/ // mockActor is a mock implementation of the shuffleSrv.Actor interface type mockActor struct{} @@ -116,6 +55,7 @@ func (a mockActor) Shuffle(formID []byte) error { // Return nil to indicate that the shuffle was successful return nil } +///////////////////////////////////////////mockPK/////////////////////////////////////////// type mockPK struct { // This field can be used to store any data that the mock public key needs to @@ -219,33 +159,5 @@ func (m mockPK) UnmarshalFrom(r io.Reader) (int, error) { -func TestEditShuffle(t *testing.T) { - // Create a new shuffle instance with a mock actor and public key - shuffle := NewShuffle(mockActor{}, mockPK{}) - - // Create a new HTTP request with the "shuffle" action and a valid formID - req, err := http.NewRequest("POST", "/shuffle", strings.NewReader(`{"action": "shuffle", "formID": "123456"}`)) - if err != nil { - t.Fatalf("Error creating request: %v", err) - } - - // Create a new HTTP response recorder to record the response - rr := httptest.NewRecorder() - - // Call the EditShuffle function with the request and response recorder - shuffle.EditShuffle(rr, req) - - // Check the status code of the response - if status := rr.Code; status != http.StatusOK { - t.Errorf("EditShuffle returned wrong status code: got %v want %v", - status, http.StatusOK) - } - - // Check the response body - expected := `{"success": true}` - if rr.Body.String() != expected { - t.Errorf("EditShuffle returned unexpected body: got %v want %v", - rr.Body.String(), expected) - } - } + From 17cdaf10c43fb46f134ac86eb9920d99a2f57729 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 13 Dec 2022 23:33:06 +0100 Subject: [PATCH 54/55] txn tests --- proxy/transaction_test.go | 124 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 proxy/transaction_test.go diff --git a/proxy/transaction_test.go b/proxy/transaction_test.go new file mode 100644 index 000000000..692881a3e --- /dev/null +++ b/proxy/transaction_test.go @@ -0,0 +1,124 @@ +package proxy + +import ( + "encoding/hex" + "net/http" + "testing" + + // "github.com/dedis/d-voting/contracts/evoting" + //"github.com/dedis/d-voting/contracts/evoting/types" + //"github.com/dedis/d-voting/internal/testing/fake" + // ptypes "github.com/dedis/d-voting/proxy/types" + "go.dedis.ch/dela/core/access" + "go.dedis.ch/dela/serde" + "go.dedis.ch/kyber/v3" + // "github.com/stretchr/testify/mock" + // "com.zerolog.logger" +) + + + +func IsTxnIncludedTest(t *testing.T, w http.ResponseWriter, r *http.Request) { + // TODO +/// h := initForm() + + //creation of the txn + //txnID, lastBlock, err := h.submitTxn(r.Context(), evoting.CmdCastVote, evoting.FormArg, data) + //if err != nil { +// http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError) +// return +// } + + //send the transaction +// h.sendTransactionInfo(w, txnID, lastBlock, ptypes.UnknownTransactionStatus) + + + + +} + + +//------------------------------------MOCK CREATION------------------------------------// + +type fakeDKG struct { + actor fakeDkgActor + err error +} + +type fakeDkgActor struct { + publicKey kyber.Point + err error +} + +var dummyFormIDBuff = []byte("dummyID") +var fakeFormID = hex.EncodeToString(dummyFormIDBuff) + +type fakeAccess struct { + // A package that is used to access the blockchain. + access.Service + + err error +} + +type fakeAuthorityFactory struct { + serde.Factory +} +/*defining the behavior of the mock logger +// create a mock zeroLog.Logger +var mockLogger = new(mockLogger.MockLogger) +var mockContext = new(mockContext.MockContext) +var mockFactory = new(mockFactory.MockFactory) + +func main() { + // define the behavior of the mock logger + mockLogger.On("IsTraceEnabled").Return(true) + mockLogger.On("IsDebugEnabled").Return(true) + mockLogger.On("IsInfoEnabled").Return(true) + mockLogger.On("IsWarnEnabled").Return(true) + mockLogger.On("IsErrorEnabled").Return(true) +} + + +func initForm( mockLogger logger, mockContext serde.Context, mockFactory serde.Factory) (types.Form) { + fakeDkg := fakeDKG{ + actor: fakeDkgActor{}, + err: nil, + } + + //real + + dummyForm := form{ + sync.Mutex : sync.Mutex{}, + + orderingSvc : fake.OrderingService{}, + logger : mockLogger, + context : mockContext, + formFac : mockFactory, + mngr txn.Manager + pool pool.Pool + pk kyber.Point + blocks blockstore.BlockStore + signer crypto.Signer + + + FormID: fakeFormID, + Status: 0, + Pubkey: nil, + Suffragia: types.Suffragia{}, + ShuffleInstances: make([]types.ShuffleInstance, 0), + DecryptedBallots: nil, + ShuffleThreshold: 0, + Roster: fake.Authority{}, + } + + //fake + + var evotingAccessKey = [32]byte{3} + rosterKey := [32]byte{} + + service := fakeAccess{err: fake.GetError()} + rosterFac := fakeAuthorityFactory{} + + return dummyForm +} +*/ \ No newline at end of file From 4f0bb4486a57b08f18253b51f9229adf4d1ac215 Mon Sep 17 00:00:00 2001 From: mamine2207 Date: Tue, 13 Dec 2022 23:36:02 +0100 Subject: [PATCH 55/55] comment shortened --- proxy/shuffle_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proxy/shuffle_test.go b/proxy/shuffle_test.go index 1aa66c7ca..d3c7358a5 100644 --- a/proxy/shuffle_test.go +++ b/proxy/shuffle_test.go @@ -44,8 +44,9 @@ func TestEditShuffle(t *testing.T) { } } -//------------------------------------------------------------MOCKS------------------------------------------------------------ -///////////////////////////////////////////mockActor/////////////////////////////////////////// +//------------------------------------------------------------MOCKS------------ + +///////////////////////////////////////////mockActor/////////////////////////// // mockActor is a mock implementation of the shuffleSrv.Actor interface type mockActor struct{} @@ -55,7 +56,7 @@ func (a mockActor) Shuffle(formID []byte) error { // Return nil to indicate that the shuffle was successful return nil } -///////////////////////////////////////////mockPK/////////////////////////////////////////// +///////////////////////////////////////////mockPK////////////////////////////// type mockPK struct { // This field can be used to store any data that the mock public key needs to