A language for collaboratively summoning computations.
Based on ValueScript.
cargo build # Note: Not a rust user? You might prefer the NodeJS version further down
export PATH="$PATH:$PWD/target/debug"
summonc main.ts
This will generate the circuit in
bristol format at
output/circuit.txt
and a description of the inputs, outputs, and constants at
output/circuit_info.json
.
You can also produce boolean circuits by adding --boolify-width 16
. (See
boolify for more about boolean circuits.)
Runs via WebAssembly so you don't need a rust environment on your system.
See
@mpc-cli/summon
.
npx @mpc-cli/summon main.ts
or
npm i -g @mpc-cli/summon
summonc main.ts
There is also a NodeJS API:
summon-ts
.
// 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
# output/circuit.txt
2 3
1 1
1 1
2 1 0 0 1 AAdd
2 1 1 0 2 AAdd
// output/mpc_settings.json
[
{
"name": "alice",
"inputs": ["input"],
"outputs": ["res"]
}
]
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.
- 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
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
In Summon you have access to a special global called summon
. If you're running
TypeScript, you can copy summon.d.ts
into your project to get accurate type
information.
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.