Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deploying to devnet/mainnet-beta: the modexp-free build

The default domain_program.so — the one the surfpool test harness and every cargo build-sbf build produce — cannot be deployed to devnet or mainnet-beta today. Not because of anything wrong with it, but because of a single syscall the DNSSEC-proof instructions reference. This page explains why, and how to build a .so that does deploy there when you need to.

Historical note: before the domain-program split, the DNSSEC-proof instructions — and this whole problem — lived in mail_program, and the modexp-free build was the only way to get any mail-program instruction (above all the postmaster reclaim tool) onto those clusters. Since the split, mail_program and alias_program are unconditionally modexp-free — their default builds deploy everywhere, no feature juggling — and only the domain program carries the proof path and its syscall.

Why the default build won’t deploy

When you deploy an SBF program, the Solana ELF loader resolves every syscall referenced anywhere in the binary, atomically, at deploy time. If the binary names a syscall the target cluster does not have active, the whole deploy is rejected — the loader will not deploy a program that references a syscall it cannot honour, even in a code path you never intend to call.

The DNSSEC-proof instructions — AuthorizeDomainByProof, WriteProofWitness, CloseProofWitness, and the three …ReclaimByProof variants — verify RSA-2048 RRSIG signatures with the sol_big_mod_exp syscall (RSA modular exponentiation; see the proving-behaviour design note for the full chain-walk). That syscall is gated behind Solana’s enable_big_mod_exp_syscall feature, which is inactive on devnet and mainnet-beta today. So the default domain_program.so, which contains those instructions, is undeployable there — the loader rejects it on the sol_big_mod_exp reference alone. A single undeployable instruction blocks everything: a program is deployed as a whole, so the whole domain registry — CreateDomain, the marketplace, the lot — is held up by the proof path’s syscall.

The fix: --no-default-features

The proof path lives behind the domain program’s dnssec-proof Cargo feature, which is on by default. Turning it off drops the six proof instructions from the binary — and with them the only sol_big_mod_exp reference — so the resulting .so deploys cleanly on devnet/mainnet-beta:

cargo build-sbf --manifest-path domain_program/Cargo.toml --no-default-features
solana program deploy target/deploy/domain_program.so

In this build the six proof instructions stay in the (unchanging) on-chain ABI enum — the discriminants do not move — but their handlers are compiled out. A transaction that sends one is rejected at runtime with InvalidInstructionData rather than dispatched. Every other instruction, including AdminCloseAccount, is present and behaves exactly as in the default build.

Note: the default (feature-on) build is unchanged and remains the one the surfpool/test harness deploys and the shipping bytecode on any cluster where enable_big_mod_exp_syscall is active. --no-default-features is a deploy-target build, not a new default. mail_program and alias_program need no equivalent — their default builds contain no sol_big_mod_exp reference at all.

Verifying the binary is modexp-free

You can confirm the sol_big_mod_exp reference is truly gone — rather than trust the build flag — by dumping the ELF’s dynamic symbols:

llvm-readelf --dyn-syms target/deploy/domain_program.so | grep -i mod_exp

The default build lists sol_big_mod_exp; the --no-default-features build prints nothing. Zero matches is the deployable state. The same check against target/deploy/mail_program.so prints nothing on every build — that program is unconditionally modexp-free since the split.

The honest trade-off

A modexp-free binary is a deploy-unblock for the non-proof domain instructions, not a way to ship DNSSEC proof without RSA. Be clear-eyed about what it gives up:

  • It cannot authorize or reclaim domains by proof at all — those six instructions return InvalidInstructionData. On such a cluster, domain authorization falls back to the delegate-signed CreateDomain path.
  • Even if the six instructions were somehow reached, a binary without sol_big_mod_exp can only verify a DNSSEC chain in which every link — including the ICANN root — signs with ECDSA P-256 (algorithm 13) or Ed25519 (algorithm 15), never RSA. The root KSK and most TLDs sign with RSA/SHA-256 (algorithm 8) today, so the modexp-free build has near-zero real-world domain-authorization-by-proof coverage. It is not a leaner DNSSEC verifier; it is a DNSSEC verifier with its most-used algorithm removed.

So this build exists for exactly one job: getting the domain registry’s non-proof instructions — domain lifecycle, the marketplace, the reclaim tool — onto a cluster where the proof path’s syscall is not yet available. It is not a configuration you would run a proof-carrying deployment on.

When to revisit

This whole workaround is temporary — it exists only because enable_big_mod_exp_syscall is inactive on devnet/mainnet-beta. Check a cluster’s feature status directly:

solana feature status | grep -i big_mod_exp

Once enable_big_mod_exp_syscall shows active on your target cluster, the default (feature-on) domain_program.so deploys there with the full DNSSEC-proof path intact, and there is no further reason to build --no-default-features. The modexp-free build is a bridge for the window in which that syscall is pending, not a permanent shape of the program.

See also