Skip to content

fix(sdk): fail mock decrypts with the threshold network's error codes - #437

Open
nftpoetrist wants to merge 1 commit into
FhenixProtocol:masterfrom
nftpoetrist:fix/sdk-mock-decrypt-error-codes
Open

nftpoetrist wants to merge 1 commit into
FhenixProtocol:masterfrom
nftpoetrist:fix/sdk-mock-decrypt-error-codes

Conversation

@nftpoetrist

@nftpoetrist nftpoetrist commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • decryptForView and decryptForTx on the mocks now throw the same CofheErrorCode and apiErrorCode as the threshold network: ACPDenied / acp_denied when the ACL says no or the ACP was revoked, ACPExpired / acp_expired, ACPInvalid / acp_invalid for a bad issuer or recipient signature, and NotPubliclyAllowed / not_publicly_allowed for withoutACP() on a value that is not public.
  • Any other reason keeps the generic SealOutputFailed / DecryptFailed. The message still carries the mock's reason string.
  • Add a patch changeset for @cofhe/sdk.

Root cause

cofheMocksDecryptForView and cofheMocksDecryptForTx wrap every rejection from MockThresholdNetwork in one generic code:

  if (error != '') {
    throw new CofheError({
      code: CofheErrorCode.SealOutputFailed,
      message: `mocks querySealOutput call failed: ${error}`,
    });
  }

The production path maps the threshold network's error field to dedicated codes (apiError.ts), and the decryption lifecycle page lists them as the errors a decrypt throws. teecryptor picks those codes from the same ACL checks the mock runs (permit.rs#L682-L690, http.rs#L996-L1062):

  • isAllowedWithPermission returns false, or reverts PermissionInvalid_Disabled: 401 acp_denied
  • PermissionInvalid_Expired: acp_expired; PermissionInvalid_IssuerSignature / _RecipientSignature: acp_invalid
  • no ACP and isPubliclyAllowed false: 403 not_publicly_allowed

So an app's ACPDenied or NotPubliclyAllowed handling can't be exercised on the mocks. acp-demo-flow.test.ts already works around the difference with /PermissionInvalid_Disabled|acp_denied/.

Fix

mockRejectionToApiErrorCode(reason, withACP) in apiError.ts returns the backend code for a mock reason, and both mock functions build the error with mapApiErrorCodeToCofheErrorCode(apiErrorCode, <generic code>), as the production path does. Messages are unchanged.

Testing

  • New core/test/cofheMocksDecryptErrors.test.ts (13 cases). RED on master: 11 failed, 2 passed (the two that keep the generic code).
  • Two inherited tests in hardhat-3-plugin-test, against the real mock contracts: a contract-scoped ACP that does not cover the value gives ACPDenied, and decryptForTx().withoutACP() on a value that is not public gives NotPubliclyAllowed. RED on master: both fail with mocks querySealOutput call failed: NotAllowed / mocks decryptForTx call failed: NotAllowed. With the fix: 45 passing.
  • pnpm --filter @cofhe/sdk test: 632 passed, 35 skipped. @cofhe/react: 78 passed. hardhat-plugin-test: 102 passing.
  • pnpm check:types, pnpm lint, pnpm check:formatting (tracked files), check:circles, check:pinned-deps: pass.

Scope

Mock path only. Production decrypts and the public API are unchanged.


Note

Low Risk
Mock-only error mapping; production decrypt APIs are unchanged, with broader test coverage for ACL failure cases.

Overview
Mock decrypt paths now align with threshold-network error semantics so local/dev tests can assert the same CofheErrorCode and apiErrorCode as production.

mockRejectionToApiErrorCode maps MockThresholdNetwork rejection strings to backend codes (acp_denied, acp_expired, acp_invalid, not_publicly_allowed). cofheMocksDecryptForView and cofheMocksDecryptForTx merge the previous separate error / allowed == false branches and throw via mapApiErrorCodeToCofheErrorCode, still falling back to SealOutputFailed / DecryptFailed for unmapped reasons. Error messages are unchanged.

Coverage adds unit tests for each mapped reason plus Hardhat inherited tests for wrong-scope ACP (ACPDenied) and decryptForTx().withoutACP() on non-public ciphertext (NotPubliclyAllowed). Patch changeset for @cofhe/sdk.

Reviewed by Cursor Bugbot for commit 7687d40. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Oct 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7687d40

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@cofhe/sdk Patch
@cofhe/abi Patch
@cofhe/hardhat-3-plugin Patch
@cofhe/hardhat-plugin Patch
@cofhe/mock-contracts Patch
@cofhe/react Patch
@cofhe/site Patch
@cofhe/example-react Patch
@cofhe/foundry-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

This branch has not been deployed

No deployments
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.

1 participant