Benchmark · 2-of-3 threshold signatures
DKLS23 vs CGGMP21 — benchmark comparison
Two 2-of-3 threshold signature protocols, benchmarked on plain Ubuntu VMs in Google Cloud. DKLS23 runs from a pinned trio image; CGGMP21 is LFDT-Lockness/cggmp21@b870883.
DKG means generating a key. DSG means signing with a key that already exists. DKG and DSG were measured on three VM sizes at loads from 1 to 200 requests in flight, then again with bandwidth capped, with delay added between the machines, and with the three machines spread across three regions. Presigning was measured separately on four VM sizes.
The load always comes from a separate machine that is not one of the three parties. How this was run covers the setup and where each number comes from.
One request at a time
One request per row, no concurrency, no repeats. All four runs are on n2d-standard-16 in one region. Files: oc-single-dkls-dkg.csv, oc-single-dkls-dsg.csv, oc-single-cggmp-dkg.csv, oc-single-cggmp-dsg.csv.
- Request latency
- Timed by the machine sending the request, for one request.
- Party-VM TX and RX
- Bytes leaving and arriving at the party VMs, read from the network counters before and after the run. TX counts each packet once, where it was sent. RX is there as a cross-check on TX, not as a second measurement.
- How many VMs are read
- For CGGMP all three party VMs are read and added up. For DKLS only the VM running the client-party adapter is read, so its rows are one machine, not three. Compare rows within a protocol, not across the two.
- Three-VM CPU
- CPU counters from the three VMs, again read before and after.
- What the bytes include
- The small HTTP request and reply, plus the traffic from reading the counters. These are not pure protocol bytes.
- One sample each
- Every row is a single run. The p50, p90 and p99 columns in those four files all repeat the same one number, so there is no distribution to read.
Signing by VM size
Signing at 1, 50, 100 and 200 requests in flight, on three VM sizes, one region, no bandwidth limit. The first three charts show p99 latency by VM size; the last two show throughput.
Key generation
Key generation on the same three VM sizes: one request, then 30 seconds of 5 and 10 at a time. Here CGGMP builds its Paillier aux info during every key generation. Section 04 shows what happens when that work is done once and reused. DKLS has no such step, so its numbers are the same in both sections.
Key generation with reused aux info
CGGMP needs Paillier aux info — a key and some proofs — before it can generate a key. That work can be done once per party and saved, so later key generations skip it, which is how a production deployment would run it. Measured with bench-node --reuse-aux. Cold rows build the aux info every time, as in section 03. Warm rows reuse it.
Bandwidth caps
Signing with 100 requests in flight on n2d-standard-16, with each VM's outbound bandwidth capped at 20, 10, 5, 3, 2 and 1 Mbit/s. Scope update (2026-09-11): sub-gigabit caps are out of scope — the production baseline starts at 5 Gbit/s, so these points are archived for reference only, not a control point.
Added network delay
Signing with 50 requests in flight on n2d-standard-16, with delay added between the VMs using tc netem. The ~0 point is the real network in one zone, from section 02. This adds delay and nothing else: no jitter, no packet loss, no bandwidth change. Section 07 has real multi-region numbers.
Across three regions
The three VMs in europe-west3-a, europe-north1-a and us-east1-b, n2d-standard-16, no bandwidth limit. A real network this time, not added delay.
Presigning, offline and online
Presigning produces a presignature with no message attached; the online finish phase consumes one to sign. Both protocols use two of the three parties per operation. Every row below is sequential — one request at a time — on n2d-standard-16 in one region, with each party VM's network counters read before and after the run and corrected for 120 seconds of measured idle traffic.
Then the same harness ran 100,000 sequential DKLS presignatures on the same trio: zero errors in 6,398.0 s (1 h 46 m 38 s), 15.63 per second, average 63.86 ms and p99 65.70 ms. Per-party-VM traffic, net of idle:
Throughput vs concurrency (measured pools only)
Test grid: 10,000 and 100,000 presignatures, 4/16/32 vCPU, concurrency 1, 10, 50, 200, 400 and 800, same region. Every point in the two chart sets and in the table is a completed timed pool run on the current binary, except the rows marked †, which are estimates derived from measured rates. A red F marks the measured failure boundary: a setting where completed runs started returning request errors.
† estimated pool time derived from the measured 10k run (×10) or the measured c=1 rate; not a completed 100k run.
Cross-region pools (west3 / north1 / us-east1)
The same presign grid across three regions. DKLS signing legs cross the Atlantic (client adapter in us-east1-b calling the trio in europe-west3-a and europe-north1-a); CGGMP signs between the two European VMs. Measured cells only, table form for now; the cross-region chart set lands with the calculator refresh.
Bandwidth headroom
One DKLS23 presignature costs about 120 KiB of aggregate party egress, one CGGMP21 presignature about 88 KiB. A 1 Gbit/s link carries roughly 1,020 DKLS presigns/s, 2.3x the fastest clean DKLS cell in the grid (439/s) and 3.2x the fastest sustained 100k pool (316/s, about 0.31 Gbit/s). Compute saturates before bandwidth on any link of 1 Gbit/s or better. CGGMP21 tops out at 10.7/s, about 7.7 Mbit/s, two orders of magnitude below any network constraint.
Compute and bandwidth per transaction (32 vCPU, same region)
| Protocol | Operation | Compute per tx | Bytes per tx (one-way, all parties) | Sustained rate | Traffic at measured rate |
|---|---|---|---|---|---|
| DKLS23 | Presigning | 0.11 core-s | 120 KiB | 316 presigns/s | 0.31 Gbit/s |
| DKLS23 | Online signing round | ~7.5 ms p50 | 4.4 KB | — | — |
| CGGMP21 | Presigning | 3.0 core-s | 88 KiB | 10.7 presigns/s | 7.7 Mbit/s |
| CGGMP21 | Online signing round | ~26 ms p50 | 4–7 KB | — | — |
Per presign, CGGMP21 spends ≈27× more compute and moves ≈28% fewer bytes. Both online rounds are small (4–7 KB) and finish in tens of milliseconds.
Bytes vs the published dkls23 figures (per party)
| Operation | dkls23 site (raw) | Our measurement (end-to-end) | Note |
|---|---|---|---|
| Presigning | (t−1)·57 KB = 57 KB | ~61 KB | +7%, framing |
| Online signing | 96 B | ~2.2 KB | harness transport (~23×); the protocol itself is 96 B |
| DKG | (n−1)·53 KB = 106 KB | ~150 KB | extra trio rounds + framing |
Our end-to-end counters include WebSocket/HTTP framing, bincode headers and message signing; the published figures are protocol payload only.
How this was run
Each protocol runs three parties, one per VM. A fourth machine sends the load and is not a party.
| VM | What it runs for CGGMP21 | What it runs for DKLS23 |
|---|---|---|
| VM0 | party 0, the hub | trio-svc-first |
| VM1 | party 1, a leaf | trio-svc-second |
| VM2 | party 2, a leaf | the client party — an HTTP adapter around trio-client |
- The load generator
- A fourth machine sends every request. It is not a party, so its CPU time and its network traffic are left out of every number on this page. Two machines were used for this: a driver VM inside the same VPC, and the operator's own workstation. VM2 is always a party, never the generator.
- Where the numbers come from
- Each party VM serves a /metrics page with CPU and network-interface counters. The runner reads it before and after a run and reports the difference.
- How many VMs each protocol's counters cover
- For CGGMP the runner reads all three party VMs and adds them up. For DKLS it reads only the VM running the client-party adapter. So a DKLS CPU or byte figure is one machine and a CGGMP one is three machines added together. They are not directly comparable, and the difference is larger for CPU than the numbers alone suggest.
- What RPS means
- Successful operations divided by how long the run lasted, timed at the generator, with a fixed number of requests in flight the whole time.
- What counts as one request
- One CGGMP key generation is three requests sent at once, one CGGMP signature is two (the 2-of-3 signer set), and one DKLS operation is one call to the adapter.
- How CGGMP talks between parties
- One websocket per run of the protocol, carrying raw bincode frames, with a barrier between rounds and a thread dedicated to accepting leaf connections. Paillier primes are made when the process starts, not during a request.
- How DKLS talks between parties
- Its own trio relay. It was not changed at any point in this work.
- Failed requests
- Five cells have one or two failures out of hundreds or thousands of requests. Every other cell has none. The counts are in each table's Success column.
- The same everywhere
- 2-of-3 (t = 2, n = 3), plain Ubuntu 24.04, no Confidential Space, three parties on three VMs of the same size.
Get in touch
Unlock privacy preserving collaboration with Silence Laboratories
Book a Call© 2026 Silence Laboratories
Learn more