Skip to content

feat(contracts): add pyth pro verifier#2079

Open
chalabi2 wants to merge 3 commits into
mainfrom
chalabi/pyth-pro-verifier
Open

feat(contracts): add pyth pro verifier#2079
chalabi2 wants to merge 3 commits into
mainfrom
chalabi/pyth-pro-verifier

Conversation

@chalabi2

@chalabi2 chalabi2 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a query-only CosmWasm verifier for upgraded Pyth Core payloads. The upgraded flow replaces Wormhole guardian signature verification with the Pyth router signer model, so Akash needs a verifier that checks the router set, expected emitter, and 3-of-5 quorum.

The verifier preserves the existing VerifyVAA { vaa, block_time } query shape used by pyth.wasm. It reuses the existing VAA envelope parser, but signature verification is router-quorum based, not Wormhole guardian based.

This also wires the contract side into the v2.1.0 upgrade: the upgrade embeds and instantiates the verifier, then migrates the existing Pyth contract to use the new verifier address. Local init now follows the same path and no longer deploys the old Wormhole contract for Pyth.

Hermes/API-key changes are intentionally out of scope for this PR.

Validation

  • bash -n _run/init.sh script/wasm2go.sh
  • cargo test -p pyth
  • cargo test -p pyth-pro-verifier
  • cargo clippy -p pyth-pro-verifier --all-targets -- -D warnings
  • cargo build --release --target wasm32-unknown-unknown -p wormhole -p pyth -p pyth-pro-verifier
  • GOWORK=off go test ./upgrades/...
  • git diff --check

Note: make generate-contracts could not run locally because no Docker daemon was available, so the generated embedding was produced from local release wasm artifacts.

Adds a query-only CosmWasm verifier for upgraded Pyth Core payloads.
The verifier preserves the VerifyVAA query shape used by pyth.wasm while
checking the Pyth Pro router set, expected emitter, and 3-of-5 quorum.

Includes a live upgraded Hermes AKT/USD fixture test proving the existing
PNAU parser still accepts the upgraded payload format.

Signed-off-by: Joseph Chalabi <chalabi.joseph@gmail.com>
@chalabi2 chalabi2 requested a review from a team as a code owner July 9, 2026 05:15
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5fe25902-c9d2-43e2-96bc-f46fc0f2bac3

📥 Commits

Reviewing files that changed from the base of the PR and between 6653c83 and 45e3864.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • _run/init.sh
  • contracts/pyth-pro-verifier/Cargo.toml
  • contracts/pyth-pro-verifier/src/contract.rs
  • contracts/pyth-pro-verifier/src/lib.rs
  • contracts/pyth-pro-verifier/src/msg.rs
  • contracts/pyth/src/contract.rs
  • contracts/pyth/src/msg.rs
  • script/wasm2go.sh
  • upgrades/software/v2.1.0/contracts.go
  • upgrades/software/v2.1.0/pyth.go
  • upgrades/software/v2.1.0/upgrade.go
💤 Files with no reviewable changes (1)
  • contracts/pyth-pro-verifier/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • contracts/pyth-pro-verifier/Cargo.toml
  • contracts/pyth-pro-verifier/src/contract.rs

Walkthrough

Adds a new pyth-pro-verifier CosmWasm crate that verifies Wormhole-style VAAs against a fixed router quorum, with error/state/message types and tests. Wires this verifier into deployment scripts and the v2.1.0 upgrade handler, and extends the pyth contract's migrate to update its stored verifier address.

Changes

Pyth Pro Verifier Contract

Layer / File(s) Summary
Crate scaffolding, state, and message contracts
Cargo.toml, contracts/pyth-pro-verifier/Cargo.toml, contracts/pyth-pro-verifier/src/lib.rs, contracts/pyth-pro-verifier/src/state.rs, contracts/pyth-pro-verifier/src/msg.rs, contracts/pyth-pro-verifier/src/error.rs
Registers the crate in the workspace, defines its manifest and module layout, Config storage, message types (InstantiateMsg, RouterAddress, MigrateMsg, QueryMsg), and ContractError variants with a std_err conversion helper.
Contract entry points and signature verification
contracts/pyth-pro-verifier/src/contract.rs
Implements instantiate validation/persistence, no-op migrate, query dispatch, VAA field checks against stored config, quorum-based router signature verification, and router address derivation from recovered keys.
Contract test suite
contracts/pyth-pro-verifier/src/testing.rs
Adds helpers to generate router keys, build/sign VAA bodies, and instantiate the contract, plus tests covering quorum, wrong signatures, wrong emitter, invalid router set index, duplicate indices, and a production fixture.
Accumulator fixture test
contracts/pyth/src/accumulator.rs
Adds a hex PNAU fixture and a test asserting parsed length, price update count, merkle root, and proof verification.

Estimated code review effort: 4 (Complex) | ~60 minutes

Verifier Deployment and Upgrade Wiring

Layer / File(s) Summary
Pyth contract migration supports updating verifier address
contracts/pyth/src/msg.rs, contracts/pyth/src/contract.rs
Adds optional wormhole_contract to MigrateMsg; migrate validates and persists it into CONFIG with a new test.
Local deployment script deploys and wires verifier contract
_run/init.sh
Replaces Wormhole WASM deployment with verifier configuration, hex-to-base64 helper, artifact checks, store/instantiate logic, and updated pyth init payload/logs referencing the verifier address.
WASM-to-Go generator includes verifier artifact
script/wasm2go.sh
Adds pyth_pro_verifier.wasm to existence checks and Go generation, emitting pythProVerifierContract and its size.
v2.1.0 upgrade handler instantiates and migrates verifier
upgrades/software/v2.1.0/pyth.go, upgrades/software/v2.1.0/upgrade.go
Adds constants/structs/helpers to build verifier instantiate/migrate payloads; UpgradeHandler instantiates the verifier, migrates pyth with its address, and uses pythContractAddr for oracle sources.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Contract
  participant Config
  Client->>Contract: query(VerifyVAA { vaa, block_time })
  Contract->>Config: load stored config
  Contract->>Contract: check version/guardian_set_index/emitter
  Contract->>Contract: verify_router_signatures (recover keys, derive router_address)
  Contract-->>Client: ParsedVAA as JSON
Loading
sequenceDiagram
  participant UpgradeHandler
  participant PythProVerifier
  participant PythContract
  participant OracleParams
  UpgradeHandler->>PythProVerifier: StoreAndInstantiateContract(newPythProVerifierInstantiateMsg())
  PythProVerifier-->>UpgradeHandler: verifierResp.Address
  UpgradeHandler->>UpgradeHandler: newPythVerifierMigrationMsg(verifierResp.Address)
  UpgradeHandler->>PythContract: StoreAndMigrateContract(pythContractAddr, migrationMsg)
  UpgradeHandler->>OracleParams: set Sources = pythContractAddr
Loading

Poem

A rabbit hops through router keys,
Five signers strong, quorum with ease 🐇
New verifier burrow, deployed with care,
Old wormhole trail swapped for fresh air,
Migrate, instantiate, hop hop away!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding the Pyth Pro verifier contract.
Description check ✅ Passed The description accurately describes the verifier, upgrade wiring, and local init changes in this pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chalabi/pyth-pro-verifier

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
contracts/pyth-pro-verifier/src/contract.rs (1)

93-142: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Signature verification logic is sound.

The quorum threshold, strictly-increasing index enforcement, bounds checking, ECDSA recovery, and router address comparison are all correct. The router_address derivation follows standard Ethereum address format (Keccak256 of uncompressed EC point minus prefix, last 20 bytes).

One minor semantic issue at lines 116-118: when an individual router index exceeds config.routers.len(), TooManySignatures is returned. This is misleading — the count is valid but the index is out of bounds. Consider a dedicated InvalidRouterIndex variant or reusing InvalidRouterSetIndex.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/pyth-pro-verifier/src/contract.rs` around lines 93 - 142, The
per-signature router index bounds check in verify_router_signatures is returning
TooManySignatures when an individual index is out of range, which is the wrong
semantic error. Update that branch to use a dedicated invalid-index error such
as InvalidRouterIndex or the existing InvalidRouterSetIndex variant, and keep
the rest of the signature validation flow unchanged.
contracts/pyth-pro-verifier/src/testing.rs (1)

234-379: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test helpers are well-structured and consistent with contract logic.

body_hash (double Keccak256) matches parse_vaa's hash computation, and sign_hash (sign_prehash_recoverable) is the correct counterpart to recover_from_prehash in the contract. address_from_key correctly implements Ethereum-style address derivation. The separation of signed_vaa (index→key mapping) vs signed_vaa_with_keys (explicit keys) enables testing both valid and adversarial signer configurations.

One minor gap: no test exercises the TooManySignatures path (signer_count > routers.len()). Consider adding a case with 6 signers against the 5-router config.

🧪 Suggested test for TooManySignatures
#[test]
fn rejects_too_many_signatures() {
    let keys = router_keys();
    let deps = setup(&keys);
    let vaa = signed_vaa(
        &keys,
        &[0, 1, 2, 3, 4, 0], // 6 signers, but index 0 is reused — order check catches first
        ROUTER_SET_INDEX,
        EMITTER_CHAIN,
        EMITTER_ADDRESS,
        vec![],
    );

    let err = query(
        deps.as_ref(),
        mock_env(),
        QueryMsg::VerifyVAA {
            vaa: Binary::from(vaa),
            block_time: 0,
        },
    )
    .unwrap_err();

    // With 6 signers > 5 routers, expect TooManySignatures
    assert!(err.to_string().contains("TooManySignatures"));
}

Note: the signer indices must be strictly increasing to reach the signer_count > config.routers.len() check, so use indices like [0, 1, 2, 3, 4, 5] with 6 distinct keys.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/pyth-pro-verifier/src/testing.rs` around lines 234 - 379, Add a
test in testing.rs that covers the TooManySignatures branch in the verification
flow. Use the existing helpers setup, router_keys, signed_vaa_with_keys, and
query to build a VAA with 6 distinct signatures against the 5-router config so
it reaches the signer_count > config.routers.len() check in the contract’s
VerifyVAA path. Assert the query fails with TooManySignatures, and make sure the
signer indexes are strictly increasing so the failure comes from signature count
rather than ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@contracts/pyth-pro-verifier/src/contract.rs`:
- Around line 93-142: The per-signature router index bounds check in
verify_router_signatures is returning TooManySignatures when an individual index
is out of range, which is the wrong semantic error. Update that branch to use a
dedicated invalid-index error such as InvalidRouterIndex or the existing
InvalidRouterSetIndex variant, and keep the rest of the signature validation
flow unchanged.

In `@contracts/pyth-pro-verifier/src/testing.rs`:
- Around line 234-379: Add a test in testing.rs that covers the
TooManySignatures branch in the verification flow. Use the existing helpers
setup, router_keys, signed_vaa_with_keys, and query to build a VAA with 6
distinct signatures against the 5-router config so it reaches the signer_count >
config.routers.len() check in the contract’s VerifyVAA path. Assert the query
fails with TooManySignatures, and make sure the signer indexes are strictly
increasing so the failure comes from signature count rather than ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3323207f-57a0-4265-8717-3ccf6926765c

📥 Commits

Reviewing files that changed from the base of the PR and between d7d0205 and 6653c83.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • contracts/pyth-pro-verifier/Cargo.toml
  • contracts/pyth-pro-verifier/src/contract.rs
  • contracts/pyth-pro-verifier/src/error.rs
  • contracts/pyth-pro-verifier/src/lib.rs
  • contracts/pyth-pro-verifier/src/msg.rs
  • contracts/pyth-pro-verifier/src/state.rs
  • contracts/pyth-pro-verifier/src/testing.rs
  • contracts/pyth-pro-verifier/src/vaa.rs
  • contracts/pyth/src/accumulator.rs

@chalabi2 chalabi2 marked this pull request as draft July 9, 2026 05:28
chalabi2 added 2 commits July 8, 2026 22:28
Reuses the existing Wormhole VAA parser in the Pyth Pro verifier so the
new contract only owns router quorum and emitter validation logic. This
removes the duplicate parser added in the initial implementation.

Signed-off-by: Joseph Chalabi <chalabi.joseph@gmail.com>
Instantiate the Pyth Pro verifier during the v2.1.0 upgrade and migrate the existing Pyth contract to use that verifier address. Local init now follows the same contract path and skips the old Wormhole deployment.

Signed-off-by: Joseph Chalabi <chalabi.joseph@gmail.com>
@chalabi2 chalabi2 marked this pull request as ready for review July 9, 2026 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant