Summon
Summon is a language for creating circuits based on TypeScript. It uses the same syntax and has many of the same features, but you'll quickly discover it's rather different underneath.
Example
// examples/loopAdd.ts
const iterations = 3;
export default (io: Summon.IO) => {
const input = io.input("alice", "input", summon.number());
let res = 0;
for (let i = 0; i < iterations; i++) {
res += input;
}
io.outputPublic("res", res);
};
summonc examples/loopAdd.ts
# Note: There is also a JS api available via summon-ts. Circuit generation is
# cheap and you can embed it inside your app.
# output/circuit.txt
2 3
1 1
1 1
2 1 0 0 1 AAdd
2 1 1 0 2 AAdd
// output/circuit_info.json
{
"constants": [],
"inputs": [
{
"name": "input",
"type": "number",
"address": 0,
"width": 1
}
],
"outputs": [
{
"name": "res",
"type": "number",
"address": 2,
"width": 1
}
]
}
// output/mpc_settings.json
[
{
"name": "alice",
"inputs": ["input"],
"outputs": ["res"]
}
]
Signal-Dependent Branching
Building a circuit from a program with a fixed path is relatively straightforward. The real power of Summon is its ability to handle signal-dependent branches - where the program follows a different path depending on the input. For example:
// examples/greaterThan10.ts
export default (io: Summon.IO) => {
const x = io.input("alice", "x", summon.number());
io.outputPublic("result", greaterThan10(x));
};
function greaterThan10(x: number) {
if (x > 10) {
return 10;
}
return 0;
}
2 1 0 1 2 AGt
2 1 2 1 3 AMul
Above, the constant 10 is used for wire 1, so the circuit is output = (x > 10) * 10
.
Summon can also handle more complex branching, so you can use loops and even things like
continue
, break
, and switch
. You can also conditionally throw exceptions as long as you
catch them.
To achieve this, Summon has a general solution to handle any conditional jump instruction. A conditional jump generates a new evaluation branch, and each branch tracks a multiplier signal. Summon dynamically manages these branches and merges them when they reach the same location.
However, it is easy to write programs which branch indefinitely and never consolidate into a single fixed circuit. Programs like this become infinite loops:
for (let i = 0; i < input; i++) {
sum += i;
}
A traditional runtime can terminate shortly after i
reaches input
, but because input
isn't
known during compilation, Summon will get stuck in a loop as it adds more and more circuitry
to handle larger and larger values of input
forever.
Limitations
- You can't use a signal as an array index
- Compile-time number operations use f64
- Math functions don't work with signals
- You have to write your own versions of
Math.min
,Math.max
, etc
- You have to write your own versions of
Exercises
If you'd like to try your hand at Summon but you're not sure where to start, I have prepared some exercises you might find interesting:
- Check Supermajority
- Approval Voting
- There's a significant difficulty gap here, but there's extensions to the first two exercises that can help bridge this gap
- Sneaky Tic-Tac-Toe
- Asset Swap
- Poker Hands
Why "Summon"
The circuits generated by Summon are intended for MPC. When performing MPC, you use cryptography to collaboratively compute the output of a function without anyone seeing each other's inputs or any of the intermediary calculations. It's like summoning the result with magic.