Skip to content

Hash with the cipher suite polkadot signs under - #9

Merged
seunlanlege merged 7 commits into
mainfrom
dami/basic-cipher-suite
Aug 16, 2026
Merged

Hash with the cipher suite polkadot signs under#9
seunlanlege merged 7 commits into
mainfrom
dami/basic-cipher-suite

Conversation

@dharjeezy

@dharjeezy dharjeezy commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

ApkProof.hashToG1 hashed with the proof of possession suite, so a signature from anyone using the basic scheme lands on a different curve point and the pairing returns false with nothing to explain why. Polkadot signs with the basic scheme, which is what this library exists to verify, so that is what it hashes with now. The two prefixes differ only in the last four bytes of the 43 byte string, so the algorithm was never in question, only which constant reaches the hash.

This is a behaviour change for a PoP deployment, which would need to pin the previous version. w3f_bls exposes both, Message::new for basic and Message::new_assuming_pop for PoP, so the distinction is easy to reintroduce behind a parameter if a second suite is ever genuinely needed.

The suite is pinned by a test in both directions rather than by a comment. The contract's output has to match what w3f/bls produces under Message::new, and it has to differ from the same message hashed under PoP, so a build where the suite never reached the hash cannot pass.

Two smaller things picked up along the way. rust/ffi/build.rs links CoreFoundation and Security on macOS, without which the cgo archive fails on undefined _CFArrayCreateMutable and _SecTrustEvaluate. And rust/prover/examples/prove_from_json.rs reads circuit inputs from a json file and writes the proof back out, which is how we drive the prover from a rust project that cannot take a cgo dependency of its own.

Building without std

The verifier is pure arkworks with no cgo, so nothing in it actually needs an operating system, but it was never declared no_std and so could not be used from a substrate runtime. There is now a std feature, on by default, forwarded to the arkworks crates, sha2, sha3 and thiserror.

One thing genuinely needed replacing. round_keys cached the Poseidon2 round keys in a OnceLock, and it is called from inside permutation, so roughly twelve thousand times for a full commitment. Deriving them per call would have been ruinous, so it now uses once_cell::race::OnceBox, which is the no_std equivalent and keeps the caching. The rest was importing Vec, String, format! and vec! from alloc.

Checked with cargo check --no-default-features, a wasm32-unknown-unknown build, and the existing tests still passing under std. Measured inside wasm on a proof from a live chain, parsing the verifying key and proof takes 6.2ms and the verification itself 12.7ms, which is what makes verifying one of these in a runtime worth doing at all.

Proving more than once

Compiling the circuit and generating its keys takes about four minutes, and prove_from_json pays that on every run, so a caller proving repeatedly spends most of its time on setup it already did. rust/prover/examples/prove_serve.rs does the setup once, prints {"ready":true}, then answers one json request per line on stdin with one json response per line on stdout. Same inputs and same output as prove_from_json, so it is only the lifetime that differs. Against a live chain a proof came back in under two minutes rather than seven.

Testing

cargo test -p gnark-plonk-verifier --test bls_verify, passing.

cargo test -p gnark-plonk-verifier --test bls_verify test_full_verify -- --ignored, passing at 550,876 gas, generating a real proof and verifying a real aggregate signature end to end. That is within noise of the 550,864 the PoP path cost before, which is what you would expect when only eleven bytes of the preimage change.

Also exercised outside this repo against real signatures and validator keys from a running chain that signs with the basic scheme, end to end through verify(), and through prove_serve for several consecutive proofs on one process.

@dharjeezy
dharjeezy requested a review from seunlanlege August 11, 2026 11:22
Comment thread solidity/contracts/ApkProof.sol Outdated
@dharjeezy dharjeezy changed the title Let a deployment pick the cipher suite instead of assuming proof of possession Hash with the cipher suite polkadot signs under Aug 14, 2026
Comment thread solidity/contracts/ApkProof.sol Outdated
@seunlanlege
seunlanlege merged commit a341f84 into main Aug 16, 2026
4 checks passed
@seunlanlege
seunlanlege deleted the dami/basic-cipher-suite branch August 16, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants