diff --git a/egklib/src/commonMain/kotlin/electionguard/core/ElGamal.kt b/egklib/src/commonMain/kotlin/electionguard/core/ElGamal.kt index 703b5f4e..2b6d7a60 100644 --- a/egklib/src/commonMain/kotlin/electionguard/core/ElGamal.kt +++ b/egklib/src/commonMain/kotlin/electionguard/core/ElGamal.kt @@ -189,16 +189,9 @@ operator fun ElGamalCiphertext.plus(o: ElGamalCiphertext): ElGamalCiphertext { * Homomorphically "adds" a sequence of ElGamal ciphertexts through piecewise multiplication. * @throws ArithmeticException if the sequence is empty */ -fun Iterable.encryptedSum(): ElGamalCiphertext = - // This operation isn't defined on an empty list -- we'd have to have some way of getting - // an encryption of zero, but we don't have the public key handy -- so we'll just raise - // an exception on that, and otherwise we're fine. - asSequence() - .let { - // TODO why not return null? - it.ifEmpty { throw ArithmeticException("Cannot sum an empty list of ciphertexts") } - .reduce { a, b -> a + b } - } +fun List.encryptedSum(): ElGamalCiphertext? { + return if (this.isEmpty()) null else this.reduce { a, b -> a + b } +} /** Add two lists by component-wise multiplication */ fun List.add(other: List): List { diff --git a/egklib/src/commonMain/kotlin/electionguard/encrypt/Encryptor.kt b/egklib/src/commonMain/kotlin/electionguard/encrypt/Encryptor.kt index 4537e97e..70e3c8ad 100644 --- a/egklib/src/commonMain/kotlin/electionguard/encrypt/Encryptor.kt +++ b/egklib/src/commonMain/kotlin/electionguard/encrypt/Encryptor.kt @@ -181,7 +181,7 @@ fun PlaintextBallot.Contest.encryptContest( ): CiphertextBallot.Contest { val ciphertexts: List = encryptedSelections.map { it.ciphertext } - val ciphertextAccumulation: ElGamalCiphertext = ciphertexts.encryptedSum() + val ciphertextAccumulation: ElGamalCiphertext = ciphertexts.encryptedSum()?: 0.encrypt(jointPublicKey) val nonces: Iterable = encryptedSelections.map { it.selectionNonce } val aggNonce: ElementModQ = with(group) { nonces.addQ() } diff --git a/egklib/src/commonMain/kotlin/electionguard/preencrypt/Recorder.kt b/egklib/src/commonMain/kotlin/electionguard/preencrypt/Recorder.kt index 78588b18..b4c8f30b 100644 --- a/egklib/src/commonMain/kotlin/electionguard/preencrypt/Recorder.kt +++ b/egklib/src/commonMain/kotlin/electionguard/preencrypt/Recorder.kt @@ -87,7 +87,7 @@ class Recorder( val selections = this.makeSelections(preeContest) val texts: List = selections.map { it.ciphertext } - val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum() + val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum()?: 0.encrypt(publicKeyEG) val nonces: Iterable = selections.map { it.selectionNonce } val aggNonce: ElementModQ = with(group) { nonces.addQ() } val totalVotes = votedFor.map{ if (it) 1 else 0 }.sum() @@ -133,7 +133,7 @@ class Recorder( val combinedEncryption = mutableListOf() repeat(nselections) { idx -> val componentEncryptions : List = this.selectedVectors.map { it.encryptions[idx] } - combinedEncryption.add( componentEncryptions.encryptedSum() ) + combinedEncryption.add( componentEncryptions.encryptedSum()?: 0.encrypt(publicKeyEG) ) } // the encryption nonces are added to create suitable nonces diff --git a/egklib/src/commonMain/kotlin/electionguard/tally/AccumulateTally.kt b/egklib/src/commonMain/kotlin/electionguard/tally/AccumulateTally.kt index 42d70e32..b83b6f18 100644 --- a/egklib/src/commonMain/kotlin/electionguard/tally/AccumulateTally.kt +++ b/egklib/src/commonMain/kotlin/electionguard/tally/AccumulateTally.kt @@ -4,40 +4,43 @@ import electionguard.ballot.EncryptedBallotIF import electionguard.ballot.EncryptedTally import electionguard.ballot.ManifestIF import electionguard.ballot.EncryptedBallot.BallotState -import electionguard.core.ElGamalCiphertext -import electionguard.core.GroupContext -import electionguard.core.UInt256 -import electionguard.core.encryptedSum +import electionguard.core.* +import electionguard.util.ErrorMessages import io.github.oshai.kotlinlogging.KotlinLogging private val logger = KotlinLogging.logger("AccumulateTally") /** Accumulate the votes of EncryptedBallots, and return a new EncryptedTally. */ -// TODO what happens if there are no EncryptedBallots? -class AccumulateTally(val group : GroupContext, val manifest : ManifestIF, val name : String, val extendedBaseHash : UInt256) { - private val contests = manifest.contests.associate { it.contestId to Contest(it)} +class AccumulateTally( + val group: GroupContext, + val manifest: ManifestIF, + val name: String, + val extendedBaseHash: UInt256, + val jointPublicKey: ElGamalPublicKey, +) { + private val contests = manifest.contests.associate { it.contestId to Contest(it) } private val castIds = mutableSetOf() - fun addCastBallot(ballot: EncryptedBallotIF): Boolean { + fun addCastBallot(ballot: EncryptedBallotIF, errs: ErrorMessages): Boolean { if (ballot.state != BallotState.CAST) { - logger.warn { "Ballot ${ballot.ballotId} does not have state CAST"} + errs.add("Ballot ${ballot.ballotId} does not have state CAST") return false } - if (!this.castIds.add(ballot.ballotId)) { - logger.warn { "Ballot ${ballot.ballotId} is duplicate"} + if (ballot.electionId != extendedBaseHash) { + errs.add("Ballot ${ballot.ballotId} has wrong electionId ${ballot.electionId}") return false } - if (ballot.electionId != extendedBaseHash) { - logger.warn { "Ballot ${ballot.ballotId} has wrong electionId ${ballot.electionId}"} + if (!this.castIds.add(ballot.ballotId)) { + errs.add("Ballot ${ballot.ballotId} is duplicate") return false } for (ballotContest in ballot.contests) { val contest = contests[ballotContest.contestId] if (contest == null) { - logger.warn { "Ballot ${ballot.ballotId} has contest ${ballotContest.contestId} not in manifest"} + errs.add("Ballot ${ballot.ballotId} has contest ${ballotContest.contestId} not in manifest") } else { - contest.accumulate(ballot.ballotId, ballotContest) + contest.accumulate(ballot.ballotId, ballotContest, errs.nested("Contest ${ballotContest.contestId}")) } } return true @@ -48,14 +51,14 @@ class AccumulateTally(val group : GroupContext, val manifest : ManifestIF, val n return EncryptedTally(this.name, tallyContests, castIds.toList(), extendedBaseHash) } - private inner class Contest(val manifestContest : ManifestIF.Contest) { - private val selections = manifestContest.selections.associate { it.selectionId to Selection(it)} + private inner class Contest(val manifestContest: ManifestIF.Contest) { + private val selections = manifestContest.selections.associate { it.selectionId to Selection(it) } - fun accumulate(ballotId : String, ballotContest: EncryptedBallotIF.Contest) { + fun accumulate(ballotId: String, ballotContest: EncryptedBallotIF.Contest, errs: ErrorMessages) { for (ballotSelection in ballotContest.selections) { val selection = selections[ballotSelection.selectionId] if (selection == null) { - logger.warn { "Ballot $ballotId has illegal selection ${ballotSelection.selectionId} in contest ${ballotContest.contestId}"} + errs.add("Ballot $ballotId has illegal selection ${ballotSelection.selectionId} in contest ${ballotContest.contestId}") } else { selection.accumulate(ballotSelection.encryptedVote) } @@ -65,12 +68,13 @@ class AccumulateTally(val group : GroupContext, val manifest : ManifestIF, val n fun build(): EncryptedTally.Contest { val tallySelections = selections.values.map { it.build() } return EncryptedTally.Contest( - manifestContest.contestId, manifestContest.sequenceOrder, tallySelections) + manifestContest.contestId, manifestContest.sequenceOrder, tallySelections + ) } } - private inner class Selection(val manifestSelection : ManifestIF.Selection) { - private val ciphertextAccumulate = ArrayList() + private inner class Selection(val manifestSelection: ManifestIF.Selection) { + private val ciphertextAccumulate = mutableListOf() fun accumulate(selection: ElGamalCiphertext) { ciphertextAccumulate.add(selection) @@ -78,7 +82,9 @@ class AccumulateTally(val group : GroupContext, val manifest : ManifestIF, val n fun build(): EncryptedTally.Selection { return EncryptedTally.Selection( - manifestSelection.selectionId, manifestSelection.sequenceOrder, ciphertextAccumulate.encryptedSum(), + manifestSelection.selectionId, + manifestSelection.sequenceOrder, + ciphertextAccumulate.encryptedSum() ?: 0.encrypt(jointPublicKey), ) } } diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt index 3f815abb..ff84d99d 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyEncryptedBallots.kt @@ -100,9 +100,8 @@ class VerifyEncryptedBallots( } // Verification 6 (Adherence to vote limits) - // TODO what happens if there are no selections? val texts: List = contest.selections.map { it.encryptedVote } - val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum() + val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum()?: 0.encrypt(jointPublicKey) val cvalid = contest.proof.verify( ciphertextAccumulation, this.jointPublicKey, diff --git a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt index 0e40869f..d09b2874 100644 --- a/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt +++ b/egklib/src/commonMain/kotlin/electionguard/verifier/VerifyPreEncryptedBallots.kt @@ -85,7 +85,7 @@ fun VerifyEncryptedBallots.verifyPreencryptionShortCodes( // product of multiple pre-encryption selection vectors. component-wise I think for (idx in 0 until nselection) { val compList = cv.selectedVectors.map { it.encryptions[idx] } - val sum = compList.encryptedSum() + val sum = compList.encryptedSum()?: 0.encrypt(jointPublicKey) if (sum != selectionVector[idx]) { results.add(Err(" 18.B Contest ${contest.contestId} (contestLimit=$contestLimit) selectionVector $idx does not match product")) } diff --git a/egklib/src/commonTest/kotlin/electionguard/core/ChaumPedersenTest.kt b/egklib/src/commonTest/kotlin/electionguard/core/ChaumPedersenTest.kt index 3b9214de..ebe054d5 100644 --- a/egklib/src/commonTest/kotlin/electionguard/core/ChaumPedersenTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/core/ChaumPedersenTest.kt @@ -287,7 +287,7 @@ class ChaumPedersenTest { val nonceAccum = nonce + nonce + nonce // we're using it three times val texts: List = listOf(vote0, vote1, vote41) - val message: ElGamalCiphertext = texts.encryptedSum() + val message: ElGamalCiphertext = texts.encryptedSum()?: 0.encrypt(publicKey) val proof = message.makeChaumPedersen( @@ -329,7 +329,7 @@ class ChaumPedersenTest { val nonceAccum = nonceSequence[0] + nonceSequence[1] + nonceSequence[2] val texts: List = listOf(vote0, vote1, vote41) - val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum() + val ciphertextAccumulation: ElGamalCiphertext = texts.encryptedSum()?: 0.encrypt(publicKey) val proof = ciphertextAccumulation.makeChaumPedersen( diff --git a/egklib/src/commonTest/kotlin/electionguard/tally/RunTallyAccumulationTest.kt b/egklib/src/commonTest/kotlin/electionguard/tally/RunTallyAccumulationTest.kt index 36ef99b7..7da057a3 100644 --- a/egklib/src/commonTest/kotlin/electionguard/tally/RunTallyAccumulationTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/tally/RunTallyAccumulationTest.kt @@ -1,7 +1,17 @@ package electionguard.tally +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.unwrap +import electionguard.ballot.EncryptedTally import electionguard.cli.RunAccumulateTally +import electionguard.core.GroupContext +import electionguard.core.getSystemTimeInMillis +import electionguard.core.productionGroup +import electionguard.publish.makeConsumer +import electionguard.util.ErrorMessages import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull class RunTallyAccumulationTest { @@ -32,4 +42,23 @@ class RunTallyAccumulationTest { ) ) } + + @Test + fun runTallyAccumulationTestJsonNoBallots() { + val group = productionGroup() + val consumerIn = makeConsumer(group, "src/commonTest/data/workflow/someAvailableJson") + val initResult = consumerIn.readElectionInitialized() + val electionInit = initResult.unwrap() + val manifest = consumerIn.makeManifest(electionInit.config.manifestBytes) + + val accumulator = AccumulateTally(group, manifest, "name", electionInit.extendedBaseHash, electionInit.jointPublicKey()) + // nothing accumulated + val tally: EncryptedTally = accumulator.build() + assertNotNull(tally) + /* + tally.contests.forEach { it.selections.forEach { + assertEquals( it.encryptedVote ) // show its an encryption of zero - only by decrypting it + }} + */ + } } \ No newline at end of file diff --git a/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt b/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt index 0f79170c..f62c1388 100644 --- a/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/verifier/AttackEncryptedBallotTest.kt @@ -53,9 +53,10 @@ class AttackEncryptedBallotTest { if (showCount) { // sum it up - val accumulator = AccumulateTally(group, electionRecord.manifest(), "attackedTally", electionRecord.extendedBaseHash()!!) + val accumulator = AccumulateTally(group, electionRecord.manifest(), "attackedTally", + electionRecord.extendedBaseHash()!!, ElGamalPublicKey(electionRecord.jointPublicKey()!!)) for (encryptedBallot in mungedBallots ) { - accumulator.addCastBallot(encryptedBallot) + accumulator.addCastBallot(encryptedBallot, ErrorMessages("")) } val encryptedTally: EncryptedTally = accumulator.build() diff --git a/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt b/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt index 66f398fc..27e713e4 100644 --- a/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt +++ b/egklib/src/commonTest/kotlin/electionguard/workflow/WorkflowEncryptDecryptTest.kt @@ -83,7 +83,7 @@ class WorkflowEncryptDecryptTest { val evote3 = vote.encrypt(publicKey, group.randomElementModQ(minimum = 1)) val accum = listOf(evote1, evote2, evote3) - val eAccum = accum.encryptedSum() + val eAccum = accum.encryptedSum()?: 0.encrypt(publicKey) //decrypt val partialDecryption = eAccum.computeShare(keypair.secretKey) @@ -112,7 +112,7 @@ class WorkflowEncryptDecryptTest { // tally val accum = listOf(evote1, evote2, evote3) - val eAccum = accum.encryptedSum() + val eAccum = accum.encryptedSum()?: 0.encrypt(publicKey) //decrypt val shares = trustees.map { eAccum.pad powP it.secretKey.key } diff --git a/egklib/src/jvmMain/kotlin/electionguard/cli/RunAccumulateTally.kt b/egklib/src/jvmMain/kotlin/electionguard/cli/RunAccumulateTally.kt index 92fa627b..028f97ca 100644 --- a/egklib/src/jvmMain/kotlin/electionguard/cli/RunAccumulateTally.kt +++ b/egklib/src/jvmMain/kotlin/electionguard/cli/RunAccumulateTally.kt @@ -11,6 +11,8 @@ import electionguard.core.productionGroup import electionguard.publish.makeConsumer import electionguard.publish.makePublisher import electionguard.tally.AccumulateTally +import electionguard.util.ErrorMessages +import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.required @@ -23,6 +25,8 @@ import kotlin.math.roundToInt class RunAccumulateTally { companion object { + private val logger = KotlinLogging.logger("RunAccumulateTally") + @JvmStatic fun main(args: Array) { val parser = ArgParser("RunAccumulateTally") @@ -50,13 +54,17 @@ class RunAccumulateTally { println("RunAccumulateTally starting\n input= $inputDir\n output = $outputDir") val group = productionGroup() - runAccumulateBallots( - group, - inputDir, - outputDir, - name ?: "RunAccumulateTally", - createdBy ?: "RunAccumulateTally" - ) + try { + runAccumulateBallots( + group, + inputDir, + outputDir, + name ?: "RunAccumulateTally", + createdBy ?: "RunAccumulateTally" + ) + } catch (t: Throwable) { + logger.error { "Exception= ${t.message} ${t.stackTraceToString()}" } + } } fun runAccumulateBallots( @@ -79,10 +87,17 @@ class RunAccumulateTally { var countBad = 0 var countOk = 0 - val accumulator = AccumulateTally(group, manifest, name, electionInit.extendedBaseHash) + val accumulator = AccumulateTally(group, manifest, name, electionInit.extendedBaseHash, electionInit.jointPublicKey()) for (encryptedBallot in consumerIn.iterateAllCastBallots()) { - val ok = accumulator.addCastBallot(encryptedBallot) - if (ok) countOk++ else countBad++ + val errs = ErrorMessages("RunAccumulateTally ballotId=${encryptedBallot.ballotId}") + accumulator.addCastBallot(encryptedBallot, errs) + if (errs.hasErrors()) { + println(errs) + logger.error{ errs.toString() } + countBad++ + } else { + countOk++ + } } val tally: EncryptedTally = accumulator.build() diff --git a/egklib/src/jvmTest/kotlin/electionguard/core/SimpleBallotBenchmark.kt b/egklib/src/jvmTest/kotlin/electionguard/core/SimpleBallotBenchmark.kt index 2c1dce1b..fbb18961 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/core/SimpleBallotBenchmark.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/core/SimpleBallotBenchmark.kt @@ -24,7 +24,7 @@ fun SimplePlaintextBallot.encrypt(context: GroupContext, keypair: ElGamalKeypair val selectionsAndProofs = plaintextWithNonceAndCiphertext.mapIndexed { i, (p, n, c) -> Pair(c, c.makeChaumPedersen(p, 1, n, keypair.publicKey, proofNonces[i].toUInt256safe())) } - val encryptedSum = selectionsAndProofs.map { it.first }.encryptedSum() + val encryptedSum = selectionsAndProofs.map { it.first }.encryptedSum()?: 0.encrypt(keypair.publicKey) val nonceSum = plaintextWithNonce.map { it.second }.reduce { a, b -> a + b } val plaintextSum = selections.sum() // fun ElGamalCiphertext.rangeChaumPedersenProofKnownNonce( diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotAggregationTestVector.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotAggregationTestVector.kt index dc1aaaab..4da9445b 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotAggregationTestVector.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotAggregationTestVector.kt @@ -10,6 +10,7 @@ import electionguard.encrypt.cast import electionguard.cli.ManifestBuilder import electionguard.input.RandomBallotProvider import electionguard.tally.AccumulateTally +import electionguard.util.ErrorMessages import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString @@ -64,9 +65,9 @@ class BallotAggregationTestVector { encryptor.encrypt(ballot, ByteArray(0)) } - val accumulator = AccumulateTally(group, manifest, "makeBallotAggregationTestVector", extendedBaseHash) + val accumulator = AccumulateTally(group, manifest, "makeBallotAggregationTestVector", extendedBaseHash, ElGamalPublicKey(publicKey)) eballots.forEach { eballot -> - accumulator.addCastBallot(eballot.cast()) + accumulator.addCastBallot(eballot.cast(), ErrorMessages("")) } val tally = accumulator.build() @@ -95,11 +96,12 @@ class BallotAggregationTestVector { } val extended_base_hash = testVector.extended_base_hash.import() ?: throw IllegalArgumentException("readBallotAggregationTestVector malformed extended_base_hash") + val joint_public_key = testVector.joint_public_key.import(group) ?: throw IllegalArgumentException("readBallotAggregationTestVector malformed joint_public_key") val eballots: List = testVector.encrypted_ballots.map { it.import(group, extended_base_hash) } val manifest = EncryptedBallotJsonManifestFacade(testVector.encrypted_ballots[0]) - val accumulator = AccumulateTally(group, manifest, "makeBallotAggregationTestVector", extended_base_hash) - eballots.forEach { eballot -> accumulator.addCastBallot(eballot) } + val accumulator = AccumulateTally(group, manifest, "makeBallotAggregationTestVector", extended_base_hash, ElGamalPublicKey(joint_public_key)) + eballots.forEach { eballot -> accumulator.addCastBallot(eballot, ErrorMessages("")) } val tally = accumulator.build() testVector.expected_encrypted_tally.contests.zip(tally.contests).forEach { (expectContest, actualContest) -> diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotEncryptionTestVector.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotEncryptionTestVector.kt index 12ae405d..2ff9efab 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotEncryptionTestVector.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/BallotEncryptionTestVector.kt @@ -201,7 +201,7 @@ class BallotEncryptionTestVector { val randomCj = expectContest.expected_proof.proofs.map { it.c_nonce.import(group) ?: throw IllegalArgumentException("readBallotEncryptionTestVector malformed c_nonce") } val ciphertexts: List = actualContest.selections.map { it.ciphertext } - val contestAccumulation: ElGamalCiphertext = ciphertexts.encryptedSum() + val contestAccumulation: ElGamalCiphertext = ciphertexts.encryptedSum()?: 0.encrypt(publicKey) val nonces: Iterable = actualContest.selections.map { it.selectionNonce } val aggNonce: ElementModQ = with(group) { nonces.addQ() } val totalVotes: Int = plainContest.selections.map { it.vote }.sum() diff --git a/egklib/src/jvmTest/kotlin/electionguard/testvectors/TallyDecryptionTestVector.kt b/egklib/src/jvmTest/kotlin/electionguard/testvectors/TallyDecryptionTestVector.kt index 45ceb2de..51a6c615 100644 --- a/egklib/src/jvmTest/kotlin/electionguard/testvectors/TallyDecryptionTestVector.kt +++ b/egklib/src/jvmTest/kotlin/electionguard/testvectors/TallyDecryptionTestVector.kt @@ -135,9 +135,9 @@ class TallyDecryptionTestVector( encryptor.encrypt(ballot, ByteArray(0)) } - val accumulator = AccumulateTally(group, manifest, "makeBallotAggregationTestVector", extendedBaseHash) + val accumulator = AccumulateTally(group, manifest, "makeBallotAggregationTestVector", extendedBaseHash, ElGamalPublicKey(publicKey)) eballots.forEach { eballot -> - accumulator.addCastBallot(eballot.cast()) + accumulator.addCastBallot(eballot.cast(), ErrorMessages("")) } val encryptedTally = accumulator.build()