Skip to content

[codex] Add client protocol reference helpers#43

Merged
v0id-byte merged 2 commits into
mainfrom
codex/client-protocol-light-client
Jul 2, 2026
Merged

[codex] Add client protocol reference helpers#43
v0id-byte merged 2 commits into
mainfrom
codex/client-protocol-light-client

Conversation

@v0id-byte

Copy link
Copy Markdown
Owner

What changed

  • Added packages/core/src/client-protocol.ts as the reusable CLIENT-PROTOCOL reference layer for light clients and indexers.
  • Exposed transaction preimage/txid helpers and deterministic encrypted-memo helpers needed by non-JS golden-vector implementations.
  • Extended light-sync tests to verify headers + recent blocks + address proofs can be consumed by the client protocol layer.
  • Added scripts/client-protocol-test.ts covering CLIENT-PROTOCOL §9 vectors for address derivation, preimage bytes, txid, ed25519 signatures, shared key, fixed-nonce ENC| memo, and decrypt.
  • Documented the new reference entry points in docs/blockchain/CLIENT-PROTOCOL.md.

Why

Wallets and indexers should be able to keep only recent full blocks, verify the header chain, and lazily request older address or transaction proofs without becoming full nodes. This does not change consensus; it gives clients a small reference implementation to follow or port.

Validation

  • corepack pnpm exec tsx scripts/client-protocol-test.ts
  • corepack pnpm exec tsx scripts/light-sync-test.ts
  • corepack pnpm typecheck
  • corepack pnpm exec tsx scripts/integration.ts
  • corepack pnpm smoke

Note: local commands still print the existing /Users/v0id/.npmrc permission warning, but all checks pass.

@v0id-byte v0id-byte marked this pull request as ready for review July 1, 2026 02:34
@v0id-byte

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 88f64a2b67

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +70 to +74
const burn = tx.burn ?? 0;
debit(st, tx.from, tx.amount + tx.fee + burn);
if (burn > 0) credit(st, NULL_ADDRESS, burn);
st.nonces.set(tx.from, (st.nonces.get(tx.from) ?? 0) + 1);
credit(st, tx.to, tx.amount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match client replay to consensus escrow payouts

For chains that include red-packet or staking transactions, this transfer-only accounting diverges from the consensus state machine in Blockchain.applyTx: CLAIM|, REFUND|, and UNSTAKE| have amount === 0 but credit payouts from escrow, and SLASH| moves stake to the treasury, while this helper only deducts the sender fee and credits tx.to by zero. Any wallet/indexer using the new replayClientState()/clientBalanceOf() helpers on full blocks will show incorrect balances after those already-supported transaction types appear.

Useful? React with 👍 / 👎.

Comment on lines +170 to +180
if (window.maxBlocks !== undefined && blocks.length > window.maxBlocks) {
return { ok: false, error: `recent blocks 超过窗口上限 ${window.maxBlocks}` };
}
for (const block of blocks) {
if (window.minTimestamp !== undefined && block.timestamp < window.minTimestamp) {
return { ok: false, error: `#${block.index} 早于 recent minTimestamp` };
}
const checked = verifyBlockAgainstHeaders(block, headers);
if (!checked.ok) return { ok: false, error: checked.error };
}
return { ok: true, value: blocks };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require the complete recent-block suffix

When verifying a QUERY_RECENT snapshot, this accepts any subset of eligible blocks: an empty recentBlocks array, or one with a recent block omitted, passes as long as the returned blocks are not too many and not too old. Because the headers already reveal the tip height and timestamps, this should require the exact suffix headers.slice(-maxBlocks).filter(h => h.timestamp >= minTimestamp) to be present; otherwise a node can omit a recent block containing the wallet's transaction and the client will treat the stale snapshot as verified.

Useful? React with 👍 / 👎.

Comment on lines +138 to +139
const checked = verifyHeaderChain(headers);
if (!checked.ok || checked.work === undefined) return { ok: false, error: checked.error ?? 'header 链校验失败' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce consensus difficulty for headers

For light clients this delegates to verifyHeaderChain(), which only checks that each hash satisfies the difficulty value embedded in that header; it never recomputes the consensus expectedDifficulty from the prior headers. After the last checkpoint, a peer can mine a fork with artificially lowered difficulty (even 0), pass verifyClientHeaders(), and then serve recent/proof data from a chain full nodes would reject. The client protocol verifier needs to reject headers whose difficulty is not the consensus value before trusting the tip/work.

Useful? React with 👍 / 👎.

Comment on lines +159 to +160
for (const tx of block.transactions) {
if (!verifyTransaction(tx)) return { ok: false, error: `#${block.index} 交易 ${tx.txid.slice(0, 12)} 自洽性失败` };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject non-consensus coinbase layouts

Recent blocks are accepted after only per-transaction self-checks, but verifyTransaction() allows any coinbase with a positive amount and does not enforce that there is exactly one coinbase in position 0 or that its amount is BLOCK_REWARD + fees. A peer that can mine an otherwise valid-looking header can include extra or overpaying coinbase transactions, this helper will accept the block, and replayClientState() will credit fabricated rewards that full nodes reject in Blockchain.validateChain().

Useful? React with 👍 / 👎.

const header = headers.find((h) => h.index === block.index);
if (!header) return { ok: false, error: `缺少 #${block.index} header` };
if (header.hash !== block.hash) return { ok: false, error: `#${block.index} hash 不在 header 链中` };
if (blockHeader(block).merkleRoot !== header.merkleRoot) return { ok: false, error: `#${block.index} merkleRoot 与 header 不一致` };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Compare the full block header

This only compares the returned block's Merkle root to the trusted header, leaving the other header fields in the full block object unchecked. Because verifyRecentBlocks() tests block.timestamp before calling this, a peer can take an older block, change its timestamp into the requested window while keeping hash and merkleRoot copied from the real header, and still pass this verifier. Compare the entire blockHeader(block) to the matched header, or recompute the hash from the full block header, before accepting it.

Useful? React with 👍 / 👎.

Comment on lines +138 to +139
const checked = verifyHeaderChain(headers);
if (!checked.ok || checked.work === undefined) return { ok: false, error: checked.error ?? 'header 链校验失败' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject headers beyond the local future-drift limit

This header verifier omits the consensus timestamp guard from Blockchain.validateChain() that rejects blocks with timestamp > Date.now() + MAX_FUTURE_DRIFT_MS. A peer can therefore serve a header chain containing far-future blocks that full nodes would reject, and the light client will still trust that tip and any recent blocks/proofs anchored to it. Apply the same future-drift check while walking headers before returning ok.

Useful? React with 👍 / 👎.

Comment on lines +159 to +160
for (const tx of block.transactions) {
if (!verifyTransaction(tx)) return { ok: false, error: `#${block.index} 交易 ${tx.txid.slice(0, 12)} 自洽性失败` };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject non-coinbase NULL_ADDRESS outputs

verifyTransaction() intentionally only checks transaction self-consistency, so a signed non-coinbase transfer with to === NULL_ADDRESS passes here even though full nodes reject it in Blockchain.validateChain(). If such a transaction appears in a served recent block, this verifier accepts the block and replayClientState() treats the amount as credited to the null address outside the burn path, producing state from a chain the network will not accept.

Useful? React with 👍 / 👎.

Comment on lines +220 to +223
} else if (!verifyTxInclusionProof(proof)) {
return { ok: false, error: `交易 ${proof.tx.txid.slice(0, 12)} Merkle proof 无效` };
} else if (!verifyTransaction(proof.tx)) {
return { ok: false, error: `交易 ${proof.tx.txid.slice(0, 12)} 自洽性失败` };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require headers before trusting address proofs

When callers omit headers, this path accepts a Merkle proof against the proof's self-supplied block header; verifyTxInclusionProof() does not check PoW, checkpoints, or whether that header is on the verified chain. A wallet that replays ADDRESS_PROOFS without separately passing the header chain can be given fabricated proofs and an arbitrary balance. Require verified headers here, or return an explicitly untrusted result when they are absent.

Useful? React with 👍 / 👎.

@v0id-byte v0id-byte merged commit 0d27d26 into main Jul 2, 2026
2 checks passed
@v0id-byte v0id-byte deleted the codex/client-protocol-light-client branch July 2, 2026 15:58
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