Skip to main content

Summon Major Update

· 3 min read
Andrew Morris
Team Lead

I’m excited to share the biggest Summon update yet. 🎉

1 · Streamlined IO with Summon.IO

Before

export default function main(a: number, b: number) {
const plus = a + b;
const gt = a > b;

return [plus, gt];
}

// and separately provide mpcSettings:
const mpcSettings = [
{
name: 'alice',
inputs: ['a'],
outputs: ['main[0]', 'main[1]'],
},
{
name: 'bob',
inputs: ['b'],
outputs: ['main[0]', 'main[1]'],
},
];

After

export default function main(io: Summon.IO) {
const a = io.input('alice', 'a', summon.number());
const b = io.input('bob', 'b', summon.number());

io.outputPublic('plus', a + b);
io.outputPublic('gt', a > b);
}

// mpcSettings is generated automatically

Shorter, self‑contained, and each output has a real name.

Per-party outputs are also coming, and will fit neatly into this API: io.output(partyName, outputName, value).

Type information is available via summon.d.ts:

intellisense demo

2 · Typed Inputs (now with bool)

The third argument of io.input specifies the type:

number example

bool example

bools now work properly, so you can pass true/false instead of 1/0. This is both better devX and removes unnecessary bits.

Output bools are also new, decoding correctly as true/false (the values you get out of await session.output()).

This also sets us up to support arrays/etc and grow into comprehensive typing à la zod or io‑ts.

3 · Public Inputs

Need a single program that adapts to many input sizes/participants? Public inputs let you accept these at compile time:

const N = io.inputPublic('N', summon.number());
let votes: boolean[] = [];

for (let i = 0; i < N; i++) {
const vote = io.input(`party${i}`, `vote${i}`, summon.bool());
votes.push(vote);
}

// Now calculate something from `votes`
// Eg majority, 2/3 majority, etc

Pass them via CLI:

summonc program.ts \
--public-inputs '{ "N": 10 }' \
--boolify-width 8

or the summon-ts API:

const { circuit } = summon.compile({
path: 'program.ts',
boolifyWidth: 8,
publicInputs: { N: 10 },
files: { /* ... */ },
});

See it in action: JumboSwap circuit.

4 · Faster Branch Merging

Merging has to occur whenever your program branches on signals:

const value = cond ? x : y;

Circuits can't evaluate only one side of this like CPUs do, so the Summon compiler has to emit wires for both branches and then merge them together like this:

value = merge(condA, x, condB, y)
// = (condA * x) + (condB * y) // old method
= (condA * x) XOR (condB * y) // new method

So, + became XOR which is great because XOR is almost free, but why is this allowed?

The key is that condA and condB cannot be true simultaneously. In this example we have condB == !condA, but we don't have to rely on that. These conditions are always non-overlapping - there is only ever one "real" branch with cond == 1. This means each bit of the addition cannot produce a carry and is equivalent to XOR, because XOR is 1-bit addition.

This caused some real speedups in our demos:

Join Us!

Thanks for building privacy‑preserving magic with us! 🪄