Skip to content

feat: on-chain MPT receipt proof verification - #67

Merged
raymond1203 merged 5 commits into
mainfrom
feat/receipt-mpt-verification
Feb 21, 2026
Merged

raymond1203 merged 5 commits into
mainfrom
feat/receipt-mpt-verification

Conversation

@hoddukzoa12

Copy link
Copy Markdown
Contributor

Summary

  • Stylus verifier에 verify_sharpe_proof_with_receipt() 추가: MPT proof → receipt RLP 추출 → dataset commitment 재계산 → STARK cross-check
  • On-chain MPT 검증 모듈 (mpt.rs): verify_mpt_proof(), compute_dataset_commitment_onchain(), decode_proof_nodes(), compute_constant_merkle_root()
  • RLP encoding/decoding 라이브러리 (lib/rlp.ts) — Ethereum Yellow Paper 준수
  • Merkle Patricia Trie builder + proof generator (lib/receipt-trie.ts)
  • Critical fix: 자체 keccak256 구현이 잘못된 해시 생성 → @noble/hashes로 교체. Receipt trie root가 on-chain receiptsRoot와 정확히 일치
  • RLP status 인코딩 수정: status 0 → 빈 바이트 (기존 [0x00] 잘못됨)
  • debug_getRawReceipts RPC 지원 (unavailable 시 re-encoding fallback)
  • WalletProver.tsx: 이중 검증 경로 (MPT+STARK → STARK-only fallback)
  • Verifier v5 deployed: 0x5f83f682b9b77614e2b90d75092abc97b49ae378

Architecture

Off-chain (Browser):
  1. fetchReceiptProof() → receiptRlp + MPT proofNodes + dataset_commitment
  2. STARK prove(return_bps, dataset_commitment)

On-chain (Stylus):
  1. verify_mpt_proof(receiptsRoot, key, proofNodes) → receipt RLP
  2. compute_dataset_commitment(blockHash, receiptsRoot, receiptRLP)
  3. verify_sharpe_stark(proof)
  4. Cross-check: pi[3] == merkle_root(commitment_column)

Known Limitation

GMX V2 receipt이 ~97KB (다수 이벤트 로그)로 MetaMask payload 한계 초과. 현재 STARK-only fallback으로 처리. Phase A (다음 PR) 에서 receiptHash (32B)만 전송하는 구조로 전환 예정.

Test plan

  • cargo test --features export-abi — Stylus verifier 79 tests 통과
  • cargo stylus check — WASM 유효성
  • Frontend E2E: GMX 거래 fetch → proof 생성 → on-chain 검증
  • keccak256 검증: @noble/hashes output == expected hash

@hoddukzoa12

Copy link
Copy Markdown
Contributor Author

Phase A 최종 완료 — E2E 검증 성공

추가 커밋 (295b043, cdb2364)

RPC 안정성 (295b043):

  • 모든 RPC call에 15초 AbortController timeout 추가
  • 배치 크기 10→5, 배치별 진행 로그, 배치 간 200ms 딜레이
  • RPC error response 체크 추가

Sharpe ratio 표시 + maxTrades 제한 (cdb2364):

  • Sharpe ratio를 raw trade data에서 직접 계산 (mean/stddev)
    • publicInputs[2]는 BN254 필드 원소라 Number()로 변환 불가
  • 결과 카드에 Sharpe ratio 표시 (green >= 1, orange >= 0, red < 0)
  • maxTrades 100으로 제한
    • 4206 trades × 32B = 134KB → MetaMask "oversized data" 거부
    • 100 trades × 32B = 3.2KB → 정상 동작 확인

E2E 테스트 결과

  • 지갑: 0x5C83942B7919db30634f9Bc9e0e72aD778852FC8 (Arbitrum One)
  • 4,259 PositionDecrease events 중 최근 100개 사용
  • Receipt hash fetch → STARK proof → on-chain verify 전체 파이프라인 성공
  • Commitment On-Chain 뱃지 + Sharpe ratio 정상 표시

@raymond1203 raymond1203 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

전체적으로 Phase A 아키텍처는 깔끔하고 @noble/hashes 도입, compute_constant_merkle_root() 최적화 등 좋은 선택들이 많습니다. 아래 보완 포인트 정리합니다.


CRITICAL

1. Receipt hash 진위 검증 없음 (Phase A 보안 모델)

  • 클라이언트가 제출한 receipt hash가 실제 Ethereum receipt인지 on-chain에서 검증하지 않습니다
  • 악의적 클라이언트가 가짜 hash를 넣어도 STARK 검증이 통과될 수 있습니다
  • Phase B에서 해결 예정이지만, Phase A의 보안 한계를 코드/문서에 명시적으로 기록해야 합니다

MAJOR

2. verify_sharpe_with_commitment e2e 테스트 없음

  • 프론트엔드의 주요 검증 경로인데 테스트가 0개입니다
  • 최소 1개의 통합 테스트 필요: receipt hashes → aggregate commitment → constant_merkle_root → pi[3] 바인딩 → STARK verify

3. 버전 네이밍 혼란

  • 변수명: STARK_VERIFIER_V4_ADDRESS
  • PR body: "v5" (0x5f83...)
  • CLAUDE.md: "v6"
  • 3개 버전 번호가 혼재합니다. 변수명과 주석 통일 필요

4. RLP/MPT TypeScript 코드 테스트 없음

  • lib/rlp.ts (220줄) + lib/receipt-trie.ts (601줄) = 820줄 신규 코드
  • Ethereum 표준 알고리즘 구현인데 테스트가 없어 encoding 버그 시 silent failure 위험
  • 최소 rlpEncodeReceipt, buildReceiptTrieAndProve 기본 테스트 필요

MINOR

5. 클라이언트 Sharpe ratio 표시값이 STARK 검증값과 다를 수 있음

  • WalletProver.tsx에서 자체 계산한 population Sharpe ratio를 표시하는데, STARK public inputs의 sharpe_sq_scaled와 다를 수 있습니다

6. 클라이언트 MPT 검증 실패 시 warn만 하고 TX 진행

  • receipt-proof.ts에서 client-side MPT 검증 실패 시 console.warn만 출력하고 계속 진행
  • on-chain에서도 실패할 가능성 높아 gas 낭비 TX 발생 가능. hard error 또는 사용자 경고 권장

7. hexToBytes 중복

  • lib/rlp.ts와 lib/receipt-proof.ts에 동일한 함수 존재. 공통 유틸로 통합 권장

8. maxTrades 5000→100 변경

  • 동작 변경인데 인라인 주석만 있고 PR description에 언급 없음

9. 프로덕션 console.log 다수

  • calldata estimation, Phase A info 등 디버그 로그가 남아있습니다

긍정적 평가

  • compute_constant_merkle_root() — O(log n) 최적화 정확
  • on-chain / off-chain aggregate commitment 알고리즘 일치 확인
  • @noble/hashes 도입 — 기존 커스텀 keccak 대비 훌륭한 선택
  • ~97KB → N×32B 경량화 아키텍처 깔끔

저희 쪽에서 #2, #3, #4 등 보완 작업 진행하겠습니다.

raymond1203 added a commit that referenced this pull request Feb 21, 2026
- Add 3 Rust integration tests for commitment binding logic (88 total)
- Add 24 TypeScript tests: 16 RLP + 8 receipt-trie (vitest)
- Add Phase A security documentation to verify_sharpe_with_commitment
- Rename STARK_VERIFIER_V4_ADDRESS → STARK_VERIFIER_ADDRESS
- Change "Sharpe Ratio" label to "Sharpe Ratio (est.)"
- Upgrade client-side MPT verification from warn to throw
- Remove console.log debug statements
@raymond1203
raymond1203 self-requested a review February 21, 2026 16:46
김진우 and others added 5 commits February 22, 2026 01:50
- Add verify_sharpe_proof_with_receipt() to Stylus verifier
  MPT proof → extract receipt RLP → compute commitment → STARK cross-check
- Implement on-chain MPT verification (mpt.rs): verify_mpt_proof(),
  compute_dataset_commitment_onchain(), decode_proof_nodes(),
  compute_constant_merkle_root()
- Add RLP encoding/decoding library (lib/rlp.ts) per Ethereum Yellow Paper
- Add Merkle Patricia Trie builder + proof generator (lib/receipt-trie.ts)
- Fix receipt-proof.ts: replace buggy custom keccak256 with @noble/hashes
  Receipt trie root now matches on-chain receiptsRoot exactly
- Fix RLP status encoding: status 0 → empty bytes (not [0x00])
- Add debug_getRawReceipts RPC support with re-encoding fallback
- Update WalletProver.tsx: dual verification (MPT+STARK / STARK-only fallback)
  Handle WalletConnect error objects, detect oversized payload errors
- Deploy verifier v5 at 0x5f83f682b9b77614e2b90d75092abc97b49ae378
Replace 97KB receipt RLP + MPT proof on-chain submission with
compact receipt hash aggregate. All trade receipts are now bound
via keccak hash chain (N x 32B) instead of a single receipt.

On-chain: verifySharpeWithCommitment recomputes aggregate commitment
from receipt hashes, verifies STARK proof, cross-checks pi[3].
Client: fetchReceiptHashes computes keccak256(receiptRlp) per trade.

- Add compute_commitment_from_hashes() to mpt.rs (hash chain)
- Add verify_sharpe_with_commitment() entry point to lib.rs
- Add fetchReceiptHashes/computeAggregateCommitment to receipt-proof.ts
- Add verifySharpeWithCommitment ABI to contracts.ts
- Simplify WalletProver.tsx: remove fallback logic, single code path
- Deploy v6 contract: 0x365344c7057eee248c986e4170e143f0449d943e

Resolves #68, #72
Public Arbitrum RPC can hang indefinitely on eth_getTransactionReceipt.
Add 15s AbortController timeout to all RPC calls, per-batch progress
logging, reduced batch size (10→5), and inter-batch delay to prevent
rate limiting. Also add RPC error checking to rpcCall.
- Compute Sharpe ratio from raw returnsBps (mean/stddev) instead of
  BN254 field element which produces meaningless large numbers
- Display Sharpe in result card with color coding (green>=1, orange>=0, red<0)
- Limit maxTrades to 100 to keep calldata under MetaMask limit
  (100 hashes × 32B = 3.2KB vs 4206 × 32B = 134KB that was rejected)
- 100 trades is statistically sufficient for Sharpe ratio computation
- Add 3 Rust integration tests for commitment binding logic (88 total)
- Add 24 TypeScript tests: 16 RLP + 8 receipt-trie (vitest)
- Add Phase A security documentation to verify_sharpe_with_commitment
- Rename STARK_VERIFIER_V4_ADDRESS → STARK_VERIFIER_ADDRESS
- Change "Sharpe Ratio" label to "Sharpe Ratio (est.)"
- Upgrade client-side MPT verification from warn to throw
- Remove console.log debug statements
@raymond1203
raymond1203 force-pushed the feat/receipt-mpt-verification branch from 82ba6c8 to d6d8715 Compare February 21, 2026 16:51
@raymond1203
raymond1203 merged commit 8c0bd81 into main Feb 21, 2026
4 of 5 checks passed
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.

2 participants