Validator signing is not only a key-management problem. It is also a latency problem.
During one consensus round, a validator may sign up to three message types:
- proposal, only if it is the proposer
- prevote
- precommit
If consensus moves to another round for the same height, this repeats. If the signer backend is slow, the validator can miss votes even when the node itself is healthy.
For gnoland validators using gnokms, the important question is simple: how long does one Ed25519 signature take?
A branch, chore/ledger-benchmarks from d4ryl00/gno, adds Ledger support and more detailed logging inside gnokms and gnoland to measure where time is spent.
Benchmarks were run with val-scenarios, branch chore/benchmark-scenario-tests, using binaries built from chore/ledger-benchmarks.
The setup compares local signing, gnokms+gnokey, and gnokms+ledger, with one or multiple validators signing consensus messages of different sizes.
Local signing is effectively free compared to consensus timings.
| backend | proposal avg | prevote avg | precommit avg |
|---|---|---|---|
| local | ~0.03 ms | ~0.026-0.027 ms | ~0.026-0.027 ms |
This is the baseline: pure software Ed25519 is not the bottleneck.
gnokms itself is not the bottleneck.
Measured from gnoland and gnokms logs:
gnoland: signature received from gnokms 587.104456ms
gnokms: signature received from ledger 586.959406ms
delta: ~145 us
Same with gnokey:
gnoland: 248.163 ms
gnokms: 248.070 ms
delta: ~93 us
So the remote signer framing, socket path, and gnokms wrapping are sub-millisecond. Backend choice dominates signing latency.
gnokms+gnokey measured around:
| phase | average |
|---|---|
| proposal | ~197 ms |
| prevote | ~193-196 ms |
| precommit | ~173-186 ms |
Most of this is not Ed25519. It comes from decrypting the local encrypted key file.
bcryptSecurityParameter = 12 is hard-coded in tm2/pkg/crypto/keys/armor/armor.go, and a local benchmark measured bcrypt at roughly:
BenchmarkBcryptGenerateFromPassword param-12: ~174 ms/op
So gnokey is simple and keeps the key encrypted at rest, but every signature pays a large key-decryption cost and the key is still used on the host.
Tested device:
- Ledger Nano S Plus
- firmware
1.6.0 - app:
gnoverse/tm-ledger-validator - transport: USB HID through
zondax/ledger-go
One important implementation detail: Ledger signing latency improved after increasing the APDU chunk size during communication. Before that change, signing was around ~730 ms per operation. After increasing the chunk size, the steady-state delay dropped to about ~550 ms per operation.
After increasing the APDU chunk size, measured gnokms+ledger latency was:
| phase | average |
|---|---|
| proposal | ~565 ms |
| prevote | ~584 ms |
| precommit | ~527 ms |
USB capture with Wireshark showed that the wire is not the issue:
| operation | wire | host stall | on-device | total |
|---|---|---|---|---|
getVersion |
~3 ms | ~51 ms | ~0 ms | ~54 ms |
| sign ~104 B payload | ~3 ms | ~51 ms | ~525 ms | ~580 ms |
Main finding: the Ledger signing cost is mostly on-device. gnokms overhead is negligible, and USB wire time is negligible.
This makes Ledger a questionable choice for hot-path validator consensus on chains with sub-second timing requirements. It remains useful for cold-path operations and key management.
YubiHSM 2 is a better fit than a YubiKey for continuous validator signing because it is designed as a server-side HSM.
It supports daemonized signing, object capabilities, PKCS#11/native APIs, and published signing-performance numbers.
Official YubiHSM 2 EdDSA-25519 signing table:
| algorithm / payload | average duration |
|---|---|
| EdDSA-25519, 32 B | ~105 ms |
| EdDSA-25519, 64 B | ~121 ms |
| EdDSA-25519, 128 B | ~137 ms |
| EdDSA-25519, 256 B | ~168 ms |
| EdDSA-25519, 512 B | ~229 ms |
| EdDSA-25519, 1024 B | ~353 ms |
Source: YubiHSM 2 performance table.
I only found official Ed25519 performance documentation for YubiHSM 2. I did not find equivalent official performance tables for YubiKey 5, Ledger devices, or other hardware models. Those backends need to be measured directly in the validator-signing path before drawing production conclusions.
These published numbers are much closer to gnokey than to Ledger, while keeping the private key inside dedicated hardware. The remaining work is to measure YubiHSM 2 through the actual gnokms validator-signing path.
YubiKey 5 is different from YubiHSM 2.
A YubiKey is primarily an authenticator and smart-card device. It is built for FIDO2, PIV, OpenPGP, SSH login, and user-held credentials.
It can support Ed25519 in newer firmware and applets, but that does not automatically make it a good validator signer. Unlike YubiHSM 2, I could not find an official Ed25519 signing-performance table for YubiKey 5.
A YubiKey may be useful for experiments or low-frequency signing, but it should not be treated as equivalent to a YubiHSM 2 for validator infrastructure.
| backend | measured / published latency | notes |
|---|---|---|
| local Ed25519 | ~0.03 ms | baseline |
| gnokms overhead | <1 ms | not the bottleneck |
gnokms+gnokey |
~170-200 ms | mostly bcrypt key decryption |
| YubiHSM 2 Ed25519 | ~105-137 ms for 32-128 B | official HSM performance table |
gnokms+ledger Nano S Plus |
~526-602 ms | mostly on-device |
| YubiKey 5 | unknown | must be benchmarked |
For gnoland validators, signing backend latency matters.
gnokms itself is fast enough. The real choice is the backend:
- local signing is fastest but exposes key material to the host
gnokeyis simple but pays a bcrypt cost on every signature- Ledger gives hardware isolation but is slow for consensus-path signing
- YubiHSM 2 is the most promising hardware candidate for continuous validator signing, based on its server-HSM design and published Ed25519 performance
- YubiKey 5 needs dedicated benchmarking before being considered for production use
This makes me think that we could eventually have a "gnokey daemon" that performs the key decryption once.