Conversation
…le/opacity-verifier
🔍 Crucible Security ReviewSummaryAdds Opacity Verifier example contract with Ethereum signature verification and allowlist-based key management. Educational/example contract for signature verification patterns. Security Assessment
Immunefi Pattern Check
False Report Risk
Code Quality
RecommendationApprove - Well-implemented example contract with proper signature verification. Add documentation clarifying example status. |
crucible-burnt
left a comment
There was a problem hiding this comment.
🔍 Crucible Security Review
Summary
New Opacity Verifier contract implementing Ethereum signature verification against an admin-controlled allowlist of signing keys. Critical security component for validating Opacity Network attestations on-chain.
Security Assessment
- Risk Level: Medium
Findings:
-
Signature verification logic is correct (
src/query.rs:5-24): Properly implements secp256k1 recovery → keccak256 → address derivation → allowlist check. The standard Ethereum personal_sign flow is followed. -
Admin authorization is properly enforced (
src/contract.rs:50-52): All execute messages checkinfo.sender != adminbefore proceeding. Good. -
Address normalization is sound (
src/contract.rs:7-15):normalize_addr_hexstrips0x, lowercases, validates 40-char hex, and attempts decode. This prevents case-sensitivity issues in allowlist lookups. -
⚠️ .unwrap()in query (src/query.rs:38):VERIFICATION_KEY_ALLOW_LIST.keys(...).map(|k| k.unwrap()).collect()will panic on corrupted storage. While queries don't affect state, this could be a DoS vector for RPC nodes. Consider using.filter_map(|k| k.ok())or propagating the error. -
Allowlist replacement is atomic (
src/contract.rs:62-67):UpdateAllowListclears the entire list then repopulates. If a key in the new list failsnormalize_addr_hex, the transaction reverts atomically (CosmWasm guarantee), so no partial state corruption. -
Verify is a query, not execute: Good design — no state mutation during verification. However, this means there's no on-chain event trail of verifications. If audit trail matters, consider an execute variant.
-
No migration entry point: The contract has
MigrateMsgin the schema but I don't see a#[entry_point] pub fn migrate— verify this is intentional or add it for upgradeability.
Immunefi Pattern Check
- No proto.Message singletons (Rust/CosmWasm contract)
- Signature verification uses
cosmwasm_std::Api::secp256k1_recover_pubkey— a well-tested host function - No fee bypass vectors — admin-only state mutations
- Recovery ID normalization handles all valid values (0, 1, 27, 28) — no bypass possible
False Report Risk
- The Ethereum-style signature verification could invite reports about "replay attacks" — but since this is a stateless query that returns bool, there's no replay risk. Consider documenting this.
- The
always_failgetrandom override inlib.rsis correctly required for CosmWasm determinism — not a vulnerability.
Code Quality Notes
- Clean separation of concerns (eth_crypto, query, contract, state)
- Tests use real Opacity Network public keys — good integration testing
- Error types are well-defined with proper
Fromimpls - Consider adding tests for edge cases: empty signature, short signature, invalid recovery ID
Status
Solid implementation. The .unwrap() in key iteration and missing migrate entry point are the main items to address. No critical security issues found.
Description
Contract Details
Please append all the required information below for the contract(s) being added to
contracts.json.Required fields and their descriptions:
name: Contract name (required)description: Brief description of the contract's purposecode_id: Contract code ID on mainnethash: Contract hash in UPPERCASErelease:url: URL to the release/commit (e.g., https://github.com/org/repo/releases/tag/v1.0.0)version: Version tag or first 7 chars of commit hashauthor:name: Organization nameurl: Organization website URLgovernance: "Genesis" or proposal numberdeprecated: true if contract is deprecated (mixed inline with active contracts)Example JSON structure:
{ "name": "", "description": "", "code_id": "", "hash": "", "release": { "url": "", "version": "" }, "author": { "name": "", "url": "" }, "governance": "", "deprecated": false }Finding Code ID and Hash
To find the latest code ID and hash:
Documentation Updates
The README.md is automatically generated from
contracts.json. After making changes:Ensure you have the required dependencies:
brew install jq(macOS) orapt-get install jq(Ubuntu/Debian)Run the convert script to validate and update the README:
Commit both the
contracts.jsonand generatedREADME.mdchangescontracts.json./convert.shlocally, the CI will fail with a "README out of sync" errorValidation
The
convert.shscript automatically performs these validations:If any validation fails, the script will show specific error messages to help you fix the issues.
Checklist
contracts.jsonwith all required fields./convert.shand fixed any validation errorscontracts.jsonand generatedREADME.mdare included in the commitAdditional Notes