Skip to content

Commit

Permalink
Merge pull request #418 from JohnLCaron/gradlePlugin
Browse files Browse the repository at this point in the history
Redo gradle plugin management for multiplatform.
  • Loading branch information
JohnLCaron authored Nov 3, 2023
2 parents 1808355 + 561b8c6 commit 72ca2ea
Show file tree
Hide file tree
Showing 23 changed files with 338 additions and 195 deletions.
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
kotlin("jvm") apply false
kotlin("multiplatform") apply false
}
2 changes: 1 addition & 1 deletion docs/CommandLineInterface.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Options:
--ncontests, -ncontests -> number of contests (always required) { Int }
--nselections, -nselections -> number of selections per contest (always required) { Int }
--outputType, -type [JSON] -> JSON or PROTO { String }
--outputDir, -out -> Directory to write output Manifest (always required) { String }
--outputDir, -out -> Directory to write test manifest file (always required) { String }
--help, -h -> Usage info
````

Expand Down
13 changes: 5 additions & 8 deletions egklib/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
buildscript {
repositories {
gradlePluginPortal()
mavenCentral()
}
}

plugins {
kotlin("multiplatform") version "1.9.10"
kotlin("multiplatform")
alias(libs.plugins.serialization)
application
}
Expand Down Expand Up @@ -98,6 +91,7 @@ nativeTarget.apply {
*/
}
jvmToolchain(17)
}

// val protoGenSource by extra("build/generated/source/proto")
Expand Down Expand Up @@ -149,3 +143,6 @@ configurations.forEach {

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>()
.configureEach { kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" }
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import electionguard.core.*
* All contests and selections must be present, so that an inspection of an EncryptedBallot reveals no information.
*/
data class EncryptedBallot(
override val ballotId: String,
val ballotStyleId: String, // matches a Manifest.BallotStyle
val encryptingDevice: String,
val timestamp: Long,
val codeBaux: ByteArray, // Baux in spec 2.0.0 eq 58
val confirmationCode: UInt256, // tracking code = H(B) eq 58
override val electionId : UInt256,
override val contests: List<Contest>,
override val state: BallotState,
val isPreencrypt: Boolean = false,
override val ballotId: String,
val ballotStyleId: String, // matches a Manifest.BallotStyle
val encryptingDevice: String,
val timestamp: Long,
val codeBaux: ByteArray, // Baux in spec 2.0.0 eq 58
val confirmationCode: UInt256, // tracking code = H(B) eq 58
override val electionId : UInt256,
override val contests: List<Contest>,
override val state: BallotState,
val isPreencrypt: Boolean = false,
) : EncryptedBallotIF {

init {
Expand Down
63 changes: 37 additions & 26 deletions egklib/src/commonMain/kotlin/electionguard/publish/Consumer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import electionguard.decrypt.DecryptingTrusteeIF
import electionguard.input.ManifestInputValidation
import electionguard.pep.BallotPep
import electionguard.util.ErrorMessages
import io.github.oshai.kotlinlogging.KotlinLogging

private val logger = KotlinLogging.logger("Consumer")

/** public API to read from the election record */
interface Consumer {
Expand All @@ -23,31 +26,32 @@ interface Consumer {
fun readTallyResult(): Result<TallyResult, ErrorMessages>
fun readDecryptionResult(): Result<DecryptionResult, ErrorMessages>

/** The list of devices that have encrytpted ballots. */
/** Are there any encrypted ballots? */
fun hasEncryptedBallots() : Boolean
/** The list of devices that have encrypted ballots. */
fun encryptingDevices(): List<String>
/** The encrypted ballot chain for specified device. */
fun readEncryptedBallotChain(device: String) : Result<EncryptedBallotChain, ErrorMessages>
/** Read encrypted ballots for specified devices. */
/** Read a specific file containing an encrypted ballot. */
fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
/** Read encrypted ballots for specified device. */
fun iterateEncryptedBallots(device: String, filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
/** Read all encrypted ballots for all devices. */
fun iterateAllEncryptedBallots(filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
fun iterateAllCastBallots(): Iterable<EncryptedBallot> = iterateAllEncryptedBallots{ it.state == EncryptedBallot.BallotState.CAST }
fun iterateAllSpoiledBallots(): Iterable<EncryptedBallot> = iterateAllEncryptedBallots{ it.state == EncryptedBallot.BallotState.SPOILED }
fun hasEncryptedBallots() : Boolean

/** Read all decrypted ballots, usually the challenged ones. */
fun iterateDecryptedBallots(): Iterable<DecryptedTallyOrBallot>

//// not part of the election record

/** read plaintext ballots in given directory, private data. */
fun iteratePlaintextBallots(ballotDir: String, filter : ((PlaintextBallot) -> Boolean)? ): Iterable<PlaintextBallot>
/** read trustee in given directory for given guardianId, private data. */
fun readTrustee(trusteeDir: String, guardianId: String): Result<DecryptingTrusteeIF, ErrorMessages>

/** Read all the PEP ratio ballots in the given directory. */
fun iteratePepBallots(pepDir : String): Iterable<BallotPep>
/** Read a specific file containing an encrypted ballot (eg for PEP). */
fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
}

fun makeConsumer(
Expand Down Expand Up @@ -92,29 +96,31 @@ fun makeTrusteeSource(
}
}

// specify the manifest filename, or the directory that its in. May be JSON or proto. If JSON, may be zipped.
// check that the file parses and validates ok
// TODO needs testing
fun readAndCheckManifestBytes(
group: GroupContext,
manifestDirOrFile: String,
): Triple<Boolean, Manifest, ByteArray> {
/**
* Read the manifest and check that the file parses and validates.
* @param manifestDirOrFile manifest filename, or the directory that its in. May be JSON or proto. If JSON, may be zipped
* @return isJson, manifest, manifestBytes
*/
fun readAndCheckManifest(group: GroupContext, manifestDirOrFile: String): Triple<Boolean, Manifest, ByteArray> {

val isZip = manifestDirOrFile.endsWith(".zip")
val isDirectory = isDirectory(manifestDirOrFile)
val isJson = if (isDirectory) {
manifestDirOrFile.endsWith(".zip") ||
pathExists("$manifestDirOrFile/${ElectionRecordJsonPaths.MANIFEST_FILE}")
} else {
manifestDirOrFile.endsWith(".json")
isZip || manifestDirOrFile.endsWith(".json")
}

val manifestFile = if (isDirectory) {
if (isJson) "$manifestDirOrFile/${ElectionRecordJsonPaths.MANIFEST_FILE}" else
"$manifestDirOrFile/${ElectionRecordProtoPaths.MANIFEST_FILE}"
} else if (isZip) {
ElectionRecordJsonPaths.MANIFEST_FILE
} else {
manifestDirOrFile
}

val manifestDir = if (isDirectory) {
val manifestDir = if (isDirectory || isZip) {
manifestDirOrFile
} else {
manifestDirOrFile.substringBeforeLast("/")
Expand All @@ -126,16 +132,21 @@ fun readAndCheckManifestBytes(
ConsumerProto(manifestDir, group)
}

val manifestBytes = consumer.readManifestBytes(manifestFile)
// make sure it parses
val manifest = consumer.makeManifest(manifestBytes)
// make sure it validates
val errors = ManifestInputValidation(manifest).validate()
if (errors.hasErrors()) {
println("*** ManifestInputValidation error on manifest in $manifestDirOrFile")
println("$errors")
throw RuntimeException("*** ManifestInputValidation error on manifest in $manifestDirOrFile")
try {
val manifestBytes = consumer.readManifestBytes(manifestFile)
// make sure it parses
val manifest = consumer.makeManifest(manifestBytes)
// make sure it validates
val errors = ManifestInputValidation(manifest).validate()
if (errors.hasErrors()) {
logger.error { "*** ManifestInputValidation error on manifest file= $manifestFile \n $errors" }
throw RuntimeException("*** ManifestInputValidation error on manifest file= $manifestFile \n $errors")
}
return Triple(isJson, manifest, manifestBytes)

} catch (t: Throwable) {
logger.error {"readAndCheckManifestBytes Exception= ${t.message} ${t.stackTraceToString()}" }
throw t
}

return Triple(isJson, manifest, manifestBytes)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ expect class ConsumerJson (topDir: String, group: GroupContext) : Consumer {
override fun readTallyResult(): Result<TallyResult, ErrorMessages>
override fun readDecryptionResult(): Result<DecryptionResult, ErrorMessages>

override fun hasEncryptedBallots() : Boolean
override fun encryptingDevices(): List<String>
override fun readEncryptedBallotChain(device: String) : Result<EncryptedBallotChain, ErrorMessages>
override fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
override fun iterateEncryptedBallots(device: String, filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
override fun iterateAllEncryptedBallots(filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
override fun hasEncryptedBallots() : Boolean

override fun iterateDecryptedBallots(): Iterable<DecryptedTallyOrBallot>
override fun iteratePepBallots(pepDir : String): Iterable<BallotPep>

override fun iteratePlaintextBallots(ballotDir: String, filter : ((PlaintextBallot) -> Boolean)? ): Iterable<PlaintextBallot>
override fun readTrustee(trusteeDir: String, guardianId: String): Result<DecryptingTrusteeIF, ErrorMessages>
override fun iteratePepBallots(pepDir : String): Iterable<BallotPep>

override fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,16 @@ expect class ConsumerJsonR (topDir: String, group: GroupContext) : Consumer {
override fun readTallyResult(): Result<TallyResult, ErrorMessages>
override fun readDecryptionResult(): Result<DecryptionResult, ErrorMessages>

override fun hasEncryptedBallots() : Boolean
override fun encryptingDevices(): List<String>
override fun readEncryptedBallotChain(device: String) : Result<EncryptedBallotChain, ErrorMessages>
override fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
override fun iterateEncryptedBallots(device: String, filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
override fun iterateAllEncryptedBallots(filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
override fun hasEncryptedBallots() : Boolean

override fun iterateDecryptedBallots(): Iterable<DecryptedTallyOrBallot>
override fun iteratePepBallots(pepDir : String): Iterable<BallotPep>

override fun iteratePlaintextBallots(ballotDir: String, filter : ((PlaintextBallot) -> Boolean)? ): Iterable<PlaintextBallot>
override fun readTrustee(trusteeDir: String, guardianId: String): Result<DecryptingTrusteeIF, ErrorMessages>

override fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
override fun iteratePepBallots(pepDir : String): Iterable<BallotPep>
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import electionguard.util.ErrorMessages

expect class ConsumerProto (topDir: String, groupContext: GroupContext) : Consumer {
override fun topdir() : String
override fun isJson(): Boolean
override fun isJson() : Boolean

override fun readManifestBytes(filename : String): ByteArray
override fun makeManifest(manifestBytes: ByteArray): Manifest
Expand All @@ -19,17 +19,16 @@ expect class ConsumerProto (topDir: String, groupContext: GroupContext) : Consum
override fun readTallyResult(): Result<TallyResult, ErrorMessages>
override fun readDecryptionResult(): Result<DecryptionResult, ErrorMessages>

override fun hasEncryptedBallots() : Boolean
override fun encryptingDevices(): List<String>
override fun readEncryptedBallotChain(device: String) : Result<EncryptedBallotChain, ErrorMessages>
override fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
override fun iterateEncryptedBallots(device: String, filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
override fun iterateAllEncryptedBallots(filter : ((EncryptedBallot) -> Boolean)? ): Iterable<EncryptedBallot>
override fun hasEncryptedBallots() : Boolean

override fun iterateDecryptedBallots(): Iterable<DecryptedTallyOrBallot>
override fun iteratePepBallots(pepDir : String): Iterable<BallotPep>

override fun iteratePlaintextBallots(ballotDir: String, filter : ((PlaintextBallot) -> Boolean)? ): Iterable<PlaintextBallot>
override fun readTrustee(trusteeDir: String, guardianId: String): Result<DecryptingTrusteeIF, ErrorMessages>

override fun readEncryptedBallot(ballotDir: String, ballotId: String) : Result<EncryptedBallot, ErrorMessages>
override fun iteratePepBallots(pepDir : String): Iterable<BallotPep>
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ data class ElectionRecordProtoPaths(val topDir : String) {
return "$ballotDir/$PLAINTEXT_BALLOT_FILE"
}

fun encryptedBallotPath(): String {
return "$electionRecordDir/$ENCRYPTED_BATCH_FILE"
}

fun decryptedBatchPath(): String {
return "$electionRecordDir/$DECRYPTED_BATCH_FILE"
}
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package electionguard.cli

import electionguard.core.productionGroup
import electionguard.publish.readAndCheckManifestBytes
import electionguard.publish.readAndCheckManifest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
Expand Down Expand Up @@ -43,14 +43,14 @@ class RunCreateTestManifestTest {

// get all and see if they compare equal
val group = productionGroup()
val (isJsonOrg, manifestOrg, _) = readAndCheckManifestBytes(group, "testOut/manifest/runCreateTestManifest")
val (isJsonOrg, manifestOrg, _) = readAndCheckManifest(group, "testOut/manifest/runCreateTestManifest")
assertTrue(isJsonOrg)

val (isJsonProto, manifestProto, _) = readAndCheckManifestBytes(group, "testOut/manifest/testConvertManifestFromJsonToProto")
val (isJsonProto, manifestProto, _) = readAndCheckManifest(group, "testOut/manifest/testConvertManifestFromJsonToProto")
assertFalse(isJsonProto)
assertEquals(manifestOrg, manifestProto)

val (isJsonRoundTrip, manifestRoundtrip, _) = readAndCheckManifestBytes(group, "testOut/manifest/testConvertManifestFromProtoToJson")
val (isJsonRoundTrip, manifestRoundtrip, _) = readAndCheckManifest(group, "testOut/manifest/testConvertManifestFromProtoToJson")
assertTrue(isJsonRoundTrip)
assertEquals(manifestProto, manifestRoundtrip)

Expand Down
Loading

0 comments on commit 72ca2ea

Please sign in to comment.