Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of Commitment Protocol backend for Viaduct 2.0 (Circuit IR) #795

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
cmake-build-*/

# IntelliJ
.idea/
out/

# mpeltonen/sbt-idea plugin
Expand Down
6 changes: 0 additions & 6 deletions .idea/kotlinScripting.xml

This file was deleted.

16 changes: 0 additions & 16 deletions .idea/misc.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.aplcornell.viaduct.backends

import io.github.aplcornell.viaduct.backends.aby.ABYBackend
import io.github.aplcornell.viaduct.backends.cleartext.CleartextBackend
import io.github.aplcornell.viaduct.backends.commitment.CommitmentBackend

/** Combines all back ends that support circuit code generation. */
object CircuitCodeGenerationBackend : Backend by listOf(CleartextBackend, ABYBackend).unions()
object CircuitCodeGenerationBackend : Backend by listOf(CleartextBackend, ABYBackend, CommitmentBackend).unions()
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
package io.github.aplcornell.viaduct.backends.cleartext

import com.squareup.kotlinpoet.BYTE_ARRAY
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.MemberName
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import io.github.aplcornell.viaduct.circuitcodegeneration.AbstractCodeGenerator
import io.github.aplcornell.viaduct.circuitcodegeneration.Argument
import io.github.aplcornell.viaduct.circuitcodegeneration.CodeGeneratorContext
import io.github.aplcornell.viaduct.circuitcodegeneration.UnsupportedCommunicationException
import io.github.aplcornell.viaduct.circuitcodegeneration.kotlinType
import io.github.aplcornell.viaduct.circuitcodegeneration.receiveExpected
import io.github.aplcornell.viaduct.circuitcodegeneration.receiveReplicated
import io.github.aplcornell.viaduct.circuitcodegeneration.typeTranslator
import io.github.aplcornell.viaduct.runtime.commitment.Commitment
import io.github.aplcornell.viaduct.runtime.commitment.Committed
import io.github.aplcornell.viaduct.syntax.BinaryOperator
import io.github.aplcornell.viaduct.syntax.Host
import io.github.aplcornell.viaduct.syntax.Protocol
import io.github.aplcornell.viaduct.syntax.UnaryOperator
import io.github.aplcornell.viaduct.syntax.circuit.OperatorNode
import io.github.aplcornell.viaduct.syntax.operators.Maximum
import io.github.aplcornell.viaduct.syntax.operators.Minimum
import io.github.aplcornell.viaduct.syntax.values.HostSetValue
import io.github.aplcornell.viaduct.backends.commitment.Commitment as CommitmentProtocol

class CleartextCircuitCodeGenerator(context: CodeGeneratorContext) : AbstractCodeGenerator(context) {

override fun operatorApplication(protocol: Protocol, op: OperatorNode, arguments: List<CodeBlock>): CodeBlock =
when (op.operator) {
Minimum ->
Expand Down Expand Up @@ -112,6 +124,140 @@
}
}

private fun checkPeerValues(
peers: HostSetValue,
value: CodeBlock,
valueType: TypeName,
builder: CodeBlock.Builder,
) {
val receivingPeers = peers.filter { it != context.host }
if (receivingPeers.isNotEmpty()) {
for (host in receivingPeers) builder.addStatement("%L", context.send(value, host))
builder.addStatement(
"%L",
receiveExpected(
value,
context.host,
valueType,
receivingPeers,
context,
),
)
}
}

private fun createCommitment(
source: Protocol,
target: Protocol,
argument: Argument,
builder: CodeBlock.Builder,
): CodeBlock {
require(context.host in source.hosts + target.hosts)
if (source !is Local) {
throw UnsupportedCommunicationException(source, target, argument.sourceLocation)

Check warning on line 157 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L157

Added line #L157 was not covered by tests
}
require(target is CommitmentProtocol)
if (target.cleartextHost != source.host) {
throw UnsupportedCommunicationException(source, target, argument.sourceLocation)

Check warning on line 161 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L161

Added line #L161 was not covered by tests
}

val sendingHost = target.cleartextHost
val receivingHosts = target.hashHosts
return when (context.host) {
sendingHost -> {
val tempName1 = context.newTemporary("committed")
val tempName2 = context.newTemporary("commitment")
builder.addStatement(
"val %N = %T(%L)",
tempName1,
(Committed::class).asTypeName(),
argument.value,
)
builder.addStatement(
"val %N = %N.%M()",
tempName2,
tempName1,
MemberName(Committed.Companion::class.asClassName(), "commitment"),
)
receivingHosts.forEach {
builder.addStatement("%L", context.send(CodeBlock.of("%N", tempName2), it))
}
CodeBlock.of("%N", tempName1)
}

in receivingHosts -> {
val argType = kotlinType(argument.type.shape, typeTranslator(argument.type.elementType.value))
val tempName3 = context.newTemporary("commitment")
builder.addStatement(
"val %N = %L",
tempName3,
context.receive((Commitment::class).asTypeName().parameterizedBy(argType), source.host),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The receivers need to check with each other they received the same values.

)
checkPeerValues(HostSetValue(receivingHosts), CodeBlock.of("%L.hash", tempName3), BYTE_ARRAY, builder)
CodeBlock.of("%N", tempName3)
}

else -> throw IllegalStateException()

Check warning on line 200 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L200

Added line #L200 was not covered by tests
}
}

private fun openCommitment(
source: Protocol,
target: Protocol,
argument: Argument,
builder: CodeBlock.Builder,
): CodeBlock {
require(source is CommitmentProtocol)
if (target !is Cleartext) {
throw UnsupportedCommunicationException(source, target, argument.sourceLocation)

Check warning on line 212 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L212

Added line #L212 was not covered by tests
}
require(context.host in source.hosts + target.hosts)

val argType = kotlinType(argument.type.shape, typeTranslator(argument.type.elementType.value))
val sendingHost = source.cleartextHost
val commitmentReceivingHosts = target.hosts.filter { it in source.hashHosts }
val cleartextReceivingHosts = target.hosts - commitmentReceivingHosts
val omittedHosts = source.hashHosts - target.hosts
val receivingHosts = target.hosts
return when (context.host) {
sendingHost -> {
commitmentReceivingHosts.forEach {
builder.addStatement("%L", context.send(argument.value, it))
}
cleartextReceivingHosts.forEach {
builder.addStatement("%L", context.send(CodeBlock.of("%L.value", argument.value), it))
}
CodeBlock.of("%L.value", argument.value)
}
in commitmentReceivingHosts -> {
val tempName1 = context.newTemporary("commitTemp")
builder.addStatement(
"val %N = %L.%N(%L)",
tempName1,
argument.value,
"open",
context.receive((Committed::class).asTypeName().parameterizedBy(argType), source.cleartextHost),
)
checkPeerValues(receivingHosts, CodeBlock.of(tempName1), argType, builder)
CodeBlock.of("%N", tempName1)
}
in cleartextReceivingHosts -> {
val tempName1 = context.newTemporary("cleartextTemp")
builder.addStatement(
"val %N = %L",
tempName1,
context.receive(argType, sendingHost),
)
checkPeerValues(receivingHosts, CodeBlock.of(tempName1), argType, builder)
CodeBlock.of("%N", tempName1)
}
in omittedHosts -> {
CodeBlock.of("")
}
else -> throw IllegalStateException()

Check warning on line 257 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L257

Added line #L257 was not covered by tests
}
}

override fun import(
protocol: Protocol,
arguments: List<Argument>,
Expand All @@ -127,6 +273,13 @@
CodeBlock.of("")
}
}
is CommitmentProtocol -> {
if (context.host in protocol.hosts + arg.protocol.hosts) {
openCommitment(arg.protocol, protocol, arg, builder)
} else {
CodeBlock.of("")

Check warning on line 280 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L280

Added line #L280 was not covered by tests
}
}

else -> throw UnsupportedCommunicationException(arg.protocol, protocol, arg.sourceLocation)
}
Expand All @@ -149,6 +302,13 @@
CodeBlock.of("")
}
}
is CommitmentProtocol -> {
if (context.host in protocol.hosts + arg.protocol.hosts) {
createCommitment(protocol, arg.protocol, arg, builder)
} else {
CodeBlock.of("")

Check warning on line 309 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/cleartext/CleartextCircuitCodeGenerator.kt#L309

Added line #L309 was not covered by tests
}
}

else -> throw UnsupportedCommunicationException(protocol, arg.protocol, arg.sourceLocation)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ object CommitmentBackend : Backend {

override fun codeGenerator(context: CodeGeneratorContext): CodeGenerator = CommitmentDispatchCodeGenerator(context)

override fun circuitCodeGenerator(context: CircuitCodeGeneratorContext): CircuitCodeGenerator = TODO()
override fun circuitCodeGenerator(context: CircuitCodeGeneratorContext): CircuitCodeGenerator = CommitmentCircuitCodeGenerator(context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.github.aplcornell.viaduct.backends.commitment

import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.asTypeName
import io.github.aplcornell.viaduct.circuitcodegeneration.AbstractCodeGenerator
import io.github.aplcornell.viaduct.circuitcodegeneration.Argument
import io.github.aplcornell.viaduct.circuitcodegeneration.CodeGeneratorContext
import io.github.aplcornell.viaduct.circuitcodegeneration.UnsupportedCommunicationException
import io.github.aplcornell.viaduct.circuitcodegeneration.typeTranslator
import io.github.aplcornell.viaduct.runtime.commitment.Committed
import io.github.aplcornell.viaduct.syntax.Protocol
import io.github.aplcornell.viaduct.syntax.types.ValueType
import io.github.aplcornell.viaduct.runtime.commitment.Commitment as CommitmentValue

/**
* Backend code generator for the commitment protocol for the circuit IR.
*
* Throws an UnsupportedCommunicationException when used in an input program as a computation protocol.
* This is because the commitment protocol is only a storage format and not a computation protocol.
*/
class CommitmentCircuitCodeGenerator(context: CodeGeneratorContext) : AbstractCodeGenerator(context) {
override fun paramType(protocol: Protocol, sourceType: ValueType): TypeName {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code belongs under storageType. paramType should be undefined (throw an error).

require(protocol is Commitment)
return when (context.host) {
protocol.cleartextHost -> (Committed::class).asTypeName().parameterizedBy(typeTranslator(sourceType))
in protocol.hashHosts -> (CommitmentValue::class).asTypeName().parameterizedBy(typeTranslator(sourceType))
else -> throw IllegalStateException()

Check warning on line 29 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt#L29

Added line #L29 was not covered by tests
}
}

override fun storageType(protocol: Protocol, sourceType: ValueType): TypeName {
return super.storageType(protocol, sourceType)

Check warning on line 34 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt#L34

Added line #L34 was not covered by tests
}

override fun import(protocol: Protocol, arguments: List<Argument>): Pair<CodeBlock, List<CodeBlock>> {
adityanathan marked this conversation as resolved.
Show resolved Hide resolved
if (arguments.isEmpty()) {
return Pair(CodeBlock.of(""), emptyList())
}
throw UnsupportedCommunicationException(arguments.first().protocol, protocol, arguments.first().sourceLocation)

Check warning on line 41 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt#L41

Added line #L41 was not covered by tests
}

override fun export(protocol: Protocol, arguments: List<Argument>): Pair<CodeBlock, List<CodeBlock>> {
adityanathan marked this conversation as resolved.
Show resolved Hide resolved
if (arguments.isEmpty()) {
return Pair(CodeBlock.of(""), emptyList())
}
throw UnsupportedCommunicationException(arguments.first().protocol, protocol, arguments.first().sourceLocation)

Check warning on line 48 in compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

compiler/src/main/kotlin/io/github/aplcornell/viaduct/backends/commitment/CommitmentCircuitCodeGenerator.kt#L48

Added line #L48 was not covered by tests
}
}
20 changes: 20 additions & 0 deletions compiler/tests/should-pass/circuit/cleartext/Commitment1.circuit
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
host alice
host bob
host chuck

circuit fun <> move@Local(host = alice)(a: int[]) -> b: int[] {
return a
}

circuit fun <> move2@Replication(hosts = {bob, chuck})(a: int[]) -> b: int[] {
return a
}

fun <> main() -> {
val a@Local(host = alice) = alice.input<int[]>()
val c@Commitment(sender = alice, receivers = {bob, chuck}) = move<>(a)
val d@Replication(hosts = {bob, chuck}) = move2<>(c)
val = bob.output<int[]>(d)
val = chuck.output<int[]>(d)
return
}
18 changes: 18 additions & 0 deletions compiler/tests/should-pass/circuit/cleartext/Commitment2.circuit
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
host alice
host bob

circuit fun <> move@Local(host = alice)(a: int[]) -> b: int[] {
return a
}

circuit fun <> move2@Local(host = bob)(a: int[]) -> b: int[] {
return a
}

fun <> main() -> {
val a@Local(host = alice) = alice.input<int[]>()
val c@Commitment(sender = alice, receivers = {bob}) = move<>(a)
val d@Local(host = bob) = move2<>(c)
val = bob.output<int[]>(d)
return
}
21 changes: 21 additions & 0 deletions compiler/tests/should-pass/circuit/cleartext/Commitment3.circuit
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
host alice
host bob
host chuck
host david

circuit fun <> move@Local(host = alice)(a: int[]) -> b: int[] {
return a
}

circuit fun <> move2@Replication(hosts = {bob, david})(a: int[]) -> b: int[] {
return a
}

fun <> main() -> {
val a@Local(host = alice) = alice.input<int[]>()
val c@Commitment(sender = alice, receivers = {bob, chuck}) = move<>(a)
val d@Replication(hosts = {bob, david}) = move2<>(c)
val = bob.output<int[]>(d)
val = david.output<int[]>(d)
return
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
host alice
host bob
host chuck

circuit fun <> move@Commitment(sender = alice, receivers = {bob, chuck})() -> {return}

fun <> main() -> {
val = move<>()
return
}
1 change: 1 addition & 0 deletions examples/inputs/circuit/cleartext/Commitment1-alice.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
Empty file.
Empty file.
1 change: 1 addition & 0 deletions examples/inputs/circuit/cleartext/Commitment2-alice.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
Empty file.
1 change: 1 addition & 0 deletions examples/inputs/circuit/cleartext/Commitment3-alice.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
1 change: 1 addition & 0 deletions examples/outputs/circuit/cleartext/Commitment1-bob.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
1 change: 1 addition & 0 deletions examples/outputs/circuit/cleartext/Commitment1-chuck.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
Empty file.
1 change: 1 addition & 0 deletions examples/outputs/circuit/cleartext/Commitment2-bob.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
Empty file.
1 change: 1 addition & 0 deletions examples/outputs/circuit/cleartext/Commitment3-bob.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
1 change: 1 addition & 0 deletions examples/outputs/circuit/cleartext/Commitment3-chuck.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions examples/outputs/circuit/cleartext/Commitment3-david.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
Empty file.
Empty file.
Empty file.
Loading