Skip to content

Optional burnStateMask in TokenSplit.split + tolerant CertificationResponse parsing#125

Merged
MastaP merged 2 commits into
mainfrom
feat/recoverable-engine-support
Jun 11, 2026
Merged

Optional burnStateMask in TokenSplit.split + tolerant CertificationResponse parsing#125
MastaP merged 2 commits into
mainfrom
feat/recoverable-engine-support

Conversation

@MastaP

@MastaP MastaP commented Jun 11, 2026

Copy link
Copy Markdown
Member

Closes #124

Two changes for the wallet-api program's recoverable engine (one rc release can cover both):

  1. burnStateMask?: Uint8Array on TokenSplit.split — default random (backward compatible). The burn transaction's state mask was the last internally generated random preventing a crash-resumable split: a caller that derives the mask deterministically (HKDF from wallet key + transferId) can now rebuild the byte-identical burn transaction after a failure and recover via getInclusionProof + match-verify.

  2. Tolerant CertificationResponse parsingfromJSON no longer throws InvalidJsonStructureError on status strings outside the enum. Deployed aggregators still emit STATE_ID_EXISTS (removed from the enum in aef6ffa/[TGE] Migrate minting from caller-supplied token ids to networkId + salt #115) until their own removal lands; an unknown status now parses and is treated by callers as "not accepted — probe the inclusion proof". Transitional by design: stays harmless after the aggregator catches up, and also future-proofs against statuses added later. The status type is CertificationStatus | (string & {}) so existing enum comparisons keep a shared enum type (lint-verified).

Verification: full suite 58/58 green (2 new tests: unknown-status parse/round-trip + empty-status rejection; byte-identical burn tx from a fixed mask with the random default preserved); lint + build clean; both changes verified through the packed lib/ artifact shape.

Review + rc publish: deferred to maintainers per program decision (wallet-api owner, 2026-06-11) — please sanity-check the tolerant-parsing direction against the post-#115 aggregator roadmap.

…nResponse parsing

- TokenSplit.split gains an optional burnStateMask parameter (default:
  random, backward compatible) — the last internally generated random that
  prevented rebuilding a byte-identical split after a crash. Callers doing
  crash-resumable transfers derive it deterministically.
- CertificationResponse.fromJSON no longer throws on status strings outside
  the CertificationStatus enum: an aggregator may emit statuses unknown to
  this SDK version (e.g. STATE_ID_EXISTS, removed in aef6ffa but still
  emitted by deployed aggregators until their removal lands). Unknown
  statuses parse and must be treated as 'not accepted - probe the inclusion
  proof'. The status type is CertificationStatus | (string & {}) so existing
  enum comparisons keep a shared enum type.

Tests: unknown-status parse/round-trip + empty-status rejection;
byte-identical burn transaction from a fixed mask, random default preserved.
Closes #124
@MastaP MastaP requested a review from martti007 June 11, 2026 16:12

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces two main changes: first, it relaxes the status validation in CertificationResponse to tolerate unknown status strings, preventing parsing failures when interacting with different aggregator versions. Second, it adds an optional burnStateMask parameter to TokenSplit.split to allow callers to reconstruct identical burn transactions for crash-resiliency. Corresponding unit and functional tests have been added. The feedback suggests validating that the caller-supplied burnStateMask is exactly 32 bytes long to prevent potential cryptographic or protocol errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/payment/TokenSplit.ts
Comment on lines +52 to 54
burnStateMask?: Uint8Array,
): Promise<ISplit> {
const hasher = new DataHasherFactory(HashAlgorithm.SHA256, DataHasher);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To prevent potential cryptographic or protocol errors, we should validate that the caller-supplied burnStateMask is exactly 32 bytes long (matching the length of the default random mask).

Suggested change
burnStateMask?: Uint8Array,
): Promise<ISplit> {
const hasher = new DataHasherFactory(HashAlgorithm.SHA256, DataHasher);
burnStateMask?: Uint8Array,
): Promise<ISplit> {
if (burnStateMask !== undefined && burnStateMask.length !== 32) {
throw new RangeError('The burnStateMask must be exactly 32 bytes long.');
}
const hasher = new DataHasherFactory(HashAlgorithm.SHA256, DataHasher);

Comment thread src/api/CertificationResponse.ts Outdated
*/
export class CertificationResponse {
public constructor(public readonly status: CertificationStatus) {}
public constructor(public readonly status: CertificationStatus | (string & {})) {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can assume that aggregator response is always unknown string and on comparison compare against enum

… validate burnStateMask length

- CertificationResponse.status is now plain string (the aggregator response
  is treated as an arbitrary status string; known values stay enumerated in
  CertificationStatus and comparison sites compare against them)
- TokenSplit.split rejects a burnStateMask that is not exactly 32 bytes
  (RangeError), with a test
@MastaP

MastaP commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Both review items addressed in the latest push: (1) status is plain string per @martti007 (enum kept for comparison; sites updated lint-clean); (2) burnStateMask now validates exact 32-byte length (RangeError) with a test, per the bot suggestion. Gates re-run: lint + build clean, 58/58 tests.

@MastaP MastaP merged commit 68bc1e5 into main Jun 11, 2026
1 check passed
@MastaP MastaP deleted the feat/recoverable-engine-support branch June 11, 2026 17:00
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.

Recoverable-engine base-SDK changes: optional burnStateMask in TokenSplit.split + tolerant CertificationResponse parsing

2 participants