Skip to content

fix(auth): injective signed-payload XDR encoding (A1) — lockstep with soroban-core#38#44

Open
AquiGorka wants to merge 1 commit into
mainfrom
fix/a1-signing-encoding-main
Open

fix(auth): injective signed-payload XDR encoding (A1) — lockstep with soroban-core#38#44
AquiGorka wants to merge 1 commit into
mainfrom
fix/a1-signing-encoding-main

Conversation

@AquiGorka

Copy link
Copy Markdown
Contributor

What

Replaces the legacy per-field bucket concatenation in buildAuthPayloadHash
(src/utils/auth/build-auth-payload.ts) with the canonical, order-sensitive XDR
encoding of the condition list, byte-identical to soroban-core#38's
hash_payload:

preimage = contractId_bytes ++ ToXdr(Vec<Condition>) ++ live_until_ledger (4-byte LE)
digest   = SHA-256(preimage)

ToXdr(Vec<Condition>) is reproduced as xdr.ScVal.scvVec(conditions.map(c => c.toScVal())).toXDR()
— the same variant-tagged, length-prefixed ScVal encoding the contract consumes.
The SDK returns the preimage; the P256 signer (crypto.subtle.sign, hash: SHA-256)
applies the SHA-256, mirroring the contract's step.

Why (A1)

The old bucketed layout concatenated deposits and withdraws into fixed buckets, so
ExtDeposit(X, a) and ExtWithdraw(X, a) over the same address/amount produced a
byte-identical preimage — a signed deposit could be reinterpreted as a withdraw. The
XDR encoding tags each enum variant and preserves list order, closing that collision
and binding the exact ordering the signer reviewed. Conditions are never sorted.

Cross-check test

src/utils/auth/build-auth-payload.unit.test.ts asserts SHA-256(SDK preimage) equals
the actual hash_payload digest emitted by the soroban-core#38 contract for fixed
inputs (known-answer vectors), covering:

  • single deposit, single withdraw (SDK digest == contract digest)
  • deposit vs. withdraw over the same (address, amount) → different digests (the A1 fix)
  • an ordered two-condition payload (SDK digest == contract digest)
  • reordering conditions → different digest

Verification

Coordination

Mirrors soroban-core#38 (fix/b3-hash-payload-docstring @ 97922db). Do not merge
lands lockstep with #38 once its encoding is finalized/published. Re-sync if #38 shifts.

Bumps deno.json 0.11.1 → 0.11.2.

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Replace the legacy per-field bucket concatenation in buildAuthPayloadHash
with the canonical, order-sensitive XDR encoding of the condition list,
byte-matching soroban-core#38's hash_payload:

  contractId ++ ToXdr(Vec<Condition>) ++ live_until_ledger (4-byte LE), SHA-256

The XDR encoding tags each enum variant and length-prefixes the vector, so
distinct condition lists always produce distinct preimages: an ExtDeposit(X,a)
and an ExtWithdraw(X,a) over the same address/amount now hash differently, and
reordering changes the digest. Condition order is preserved as-is (no sorting).

Adds a cross-check unit test asserting SHA-256(SDK preimage) equals the actual
contract hash_payload digest for fixed inputs (deposit, withdraw, and an ordered
multi-condition payload), using known-answer vectors emitted by soroban-core#38.

Bumps deno.json 0.11.1 -> 0.11.2. Lands lockstep with soroban-core#38.
@AquiGorka AquiGorka force-pushed the fix/a1-signing-encoding-main branch from 76618bd to ebb4dc4 Compare July 8, 2026 20:42
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