Files
pse.dev/content/articles/summon-major-update.md
Kalidou Diagne 31763f7662 feat: PageSpeed Insights improvements (#545)
* feat: PageSpeed Insights improvements
2025-09-01 06:01:00 +02:00

4.8 KiB
Raw Permalink Blame History

title, image, tldr, authors, date, tags, projects, canonical
title image tldr authors date tags projects canonical
Summon Major Update /articles/summon-major-update/cover.webp Im excited to share the biggest Summon update yet. 🎉
Andrew Morris
2025-05-21
summon
mpc
typescript
privacy
developer-tools
release-notes
circuits
cryptography
compiler
mpc-framework
https://mpc.pse.dev/blog/summon-major-update

Im excited to share the biggest Summon update yet. 🎉

Wait - What's Summon?

Summon is a dialect of TypeScript for generating boolean circuits! We use this to great effect in MPC Framework, since it requires the computation inside the MPC cryptography to be a boolean circuit. It's so much more than a TypeScript library - it's a very special runtime that transforms branching (if/else/for/while/switch) into flat circuitry.

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

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, selfcontained, 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 iots.

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)
}

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 privacypreserving magic with us! 🪄