Data Availability, Sequencer, Finality
도입
L2 결제에서 가장 위험한 문장은 "transaction이 보였으니 완료"다. wallet이나 explorer에 transaction이 나타난 상태, sequencer가 transaction을 받아들인 상태, L2 state가 확정된 상태, L1에 batch/proof가 게시된 상태, bridge나 CCTP가 최종 처리된 상태는 모두 다르다. 결제 제품은 이 차이를 가격, 금액, 환불 가능성, merchant delivery 정책으로 바꿔야 한다.
이 강의에서는 DA, sequencer, finality를 따로 외우지 않는다. checkout이 언제 Paid가 되고, settlement ledger가 언제 닫히고, stuck 상태가 언제 support ticket으로 넘어가는지 결정하는 기준으로 읽는다.
학습 목표
- data availability가 독립 검증과 자금 회수에 왜 필요한지 설명한다.
- sequencer confirmation과 L1/bridge finality를 분리해 confirmation policy를 만든다.
- 금액과 위험에 따라 soft confirmation, final confirmation, manual review 조건을 다르게 둔다.
- stuck 상태를 예외가 아니라 제품 상태로 설계한다.
개념 설명
개념 읽기
DA, sequencer, finality가 결제 확정성에 미치는 영향을 설명한다.
실패 상태 확인
finality와 attestation 조건이 명확한가
실습 산출물 작성
Confirmation policy matrix 작성
완료 기준 대조
DA와 finality 차이를 설명했다.
1. DA는 "데이터를 다시 구할 수 있는가"보다 먼저 "검증할 수 있는가"를 묻는다
Ethereum.org는 data availability를 block 검증에 필요한 데이터가 실제로 network participants에게 제공된다는 확신으로 설명한다. rollup에서는 실행은 L2에서 일어나도, 그 실행을 검증하거나 dispute하려면 transaction data가 필요하다.
| 개념 | 질문 | 제품 영향 |
|---|---|---|
| Data availability | state transition을 검증할 데이터가 공개되었는가 | fraud proof, validity proof, independent verification 가능성 |
| Data retrievability | 과거 데이터를 API/indexer로 편하게 다시 조회할 수 있는가 | dashboard, support, accounting 편의성 |
| Blob availability | blob data가 정해진 기간 동안 availability를 제공하는가 | rollup batch 검증과 monitoring window |
| Archive access | 오래된 transaction과 state를 제품이 조회할 수 있는가 | 사후 감사, customer dispute, tax/reporting |
DA가 약하면 단순히 "explorer 조회가 불편하다"가 아니다. 사용자가 자금 소유를 증명하거나 invalid state에 대응하는 능력이 약해질 수 있다. 그래서 DA는 product risk sheet에서 별도 항목으로 둔다.
2. Sequencer는 빠른 UX와 censorship risk를 동시에 만든다
sequencer는 L2 transaction ordering과 빠른 confirmation UX를 제공한다. 중앙화된 sequencer가 많은 L2에서 사용자는 빠른 체감 속도를 얻지만, 장애와 censorship 지점을 함께 갖는다.
| 상황 | checkout 영향 | 운영 판단 |
|---|---|---|
| sequencer 정상 | 빠른 L2 confirmation 가능 | 소액 결제는 soft confirmation 허용 가능 |
| sequencer 지연 | 결제 pending 증가 | route degradation, retry 안내 |
| sequencer down | 신규 checkout과 refund가 멈춤 | route disable, status page, support macro |
| L1 force path 있음 | 일부 transaction을 L1 경유로 제출 가능 | 고액/treasury 복구 경로로 문서화 |
| L1 force path 없음 | 사용자가 sequencer 복구를 기다려야 함 | public support 제한 또는 unsupported 판단 |
중요한 점은 sequencer accepted가 final settlement가 아니라는 것이다. merchant가 즉시 상품을 제공해도 되는지, refund queue를 닫아도 되는지, treasury ledger를 확정해도 되는지는 별도 기준이 필요하다.
3. Finality는 결제 목적별로 다르게 잡는다
| finality 단계 | 의미 | 적합한 사용 |
|---|---|---|
| Wallet displayed | wallet/RPC가 transaction을 보여줌 | 사용자 안내 시작, 완료 판정에는 부족 |
| Sequencer accepted | L2 sequencer가 transaction을 포함 | 낮은 금액 API payment, risk-tolerant UX |
| L2 block finalized | L2 내부 finality 또는 canonical state 기준 충족 | 일반 checkout과 refund queue 처리 |
| L1 posted | batch, calldata, blob, proof가 L1에 게시 | 고액 결제, merchant settlement, fraud window 추적 |
| Challenge/proof window passed | optimistic challenge 또는 validity proof 조건 충족 | treasury movement, final accounting |
| Bridge final | withdrawal, CCTP receive, cross-chain mint 완료 | cross-chain settlement와 merchant payout |
같은 Paid라도 내부적으로는 PaidSoft, PaidFinal, Settled, BridgeFinal처럼 나누어 관리할 수 있다. UI에는 간단히 보여주되 ledger와 risk engine에는 단계가 남아야 한다.
4. Confirmation policy는 금액과 되돌릴 수 없음으로 정한다
| 업무 | 권장 기준 | 이유 |
|---|---|---|
| low-value API payment | sequencer accepted + fraud/abuse limit | 사용자 속도가 중요하고 손실 한도가 작다 |
| normal checkout | L2 block finalized + token transfer event | 상품 제공 전 최소한의 결제 확정 필요 |
| high-value merchant settlement | L1 posted 또는 proof/finality 조건 | chargeback/회계 오류의 비용이 크다 |
| treasury withdrawal | bridge final 또는 challenge/proof window 통과 | treasury는 최종 회수 가능성을 우선한다 |
| cross-chain payout | source finality + destination receive/mint | source/destination 상태를 모두 닫아야 한다 |
정책은 chain별로 달라질 수 있다. "모든 L2는 12 confirmations" 같은 고정 규칙보다, L2 risk intake sheet의 DA, sequencer, proof, bridge 상태를 읽고 기준을 정하는 방식이 낫다.
5. Stuck 상태는 실패가 아니라 설계 대상이다
Delayed와 ManualReview가 없는 결제 상태머신은 운영에서 깨진다. 정상 지연, provider 장애, reorg, CCTP attestation 지연, bridge message 실패를 모두 "실패"로 처리하면 사용자 안내와 회계가 뒤섞인다.
코드로 확인하기
위 신뢰 모델을 코드와 운영 규칙으로 확인한다. 메시지 상태, 최종성, 재시도, 관측 지점이 어디서 분리되는지 보는 것이 목적이다.
백엔드Confirmation policy matrix — 금액·체인별 N블록 정책
금액이 크면 confirmation을 늘리고, 체인이 DA 약하면 더 보수적으로. policy를 코드 한 곳에 박는다.
type ChainConfirmationPolicy = { chainId: number; softConfirmBlocks: number; // 사용자에게 "결제 진행 중" 표시 가능 finalConfirmBlocks: number; // ledger 에서 settled 처리 largeAmountUsd: number; // 이 금액 이상이면 더 보수적인 정책 largeAmountFinalBlocks: number;};export const CONFIRMATION_POLICY: Record<number, ChainConfirmationPolicy> = { 1: { // Ethereum mainnet chainId: 1, softConfirmBlocks: 1, finalConfirmBlocks: 32, // ~ Casper finality largeAmountUsd: 100_000, largeAmountFinalBlocks: 64 }, 8453: { // Base chainId: 8453, softConfirmBlocks: 2, finalConfirmBlocks: 200, largeAmountUsd: 50_000, largeAmountFinalBlocks: 500 }, 42161: { // Arbitrum One chainId: 42161, softConfirmBlocks: 1, finalConfirmBlocks: 100, largeAmountUsd: 100_000, largeAmountFinalBlocks: 300 }};export function blocksRequired(chainId: number, amountUsd: number, kind: "soft" | "final") { const p = CONFIRMATION_POLICY[chainId]; if (!p) throw new Error(`no policy for chain ${chainId}`); if (kind === "soft") return p.softConfirmBlocks; return amountUsd >= p.largeAmountUsd ? p.largeAmountFinalBlocks : p.finalConfirmBlocks;}인덱서Confirmation 카운터 + reorg watcher
블록 N개 후 finalized 처리. block hash가 바뀌면 (reorg) status를 되돌린다.
export async function tickConfirmations(chainId: number) { const head = await client.getBlock({ blockTag: "latest" }); const pending = await prisma.payment.findMany({ where: { chainId, status: { in: ["SoftPaid", "Submitted"] } } }); for (const p of pending) { const blockAtSubmit = await client.getBlock({ blockNumber: p.submittedAtBlock }); if (blockAtSubmit.hash !== p.submittedAtBlockHash) { // reorg 발생 — 상태를 되돌린다 await prisma.payment.update({ where: { id: p.id }, data: { status: "Retryable", reorgedAt: new Date() } }); continue; } const confirmations = Number(head.number - p.submittedAtBlock); const finalBlocks = blocksRequired(chainId, p.amountUsd, "final"); if (confirmations >= finalBlocks) { await prisma.payment.update({ where: { id: p.id }, data: { status: "FinalPaid", finalizedAt: new Date() } }); } }}운영Sequencer outage 시 결제·환불·인출 분리 대응
모든 기능을 동시에 막지 않는다. 신규 결제는 차단, refund는 큐에, withdrawal은 L1 escape를 안내.
type SequencerOutageAction = { newCheckout: "disable" | "enable"; refund: "queue" | "execute"; treasuryWithdrawal: "block-l2-route" | "force-l1-escape" | "wait"; userBanner: string;};export const OUTAGE_RESPONSE: SequencerOutageAction = { newCheckout: "disable", refund: "queue", treasuryWithdrawal: "force-l1-escape", userBanner: "이 체인의 시퀀서가 응답하지 않습니다. 신규 결제는 일시 중단되었고, 환불은 복구 후 자동 처리됩니다."};강의 포인트
| 관점 | 수업 중 확인할 질문 | 산출물 |
|---|---|---|
| DA | 검증에 필요한 data가 어디에 있는가 | DA/retention/source 표 |
| sequencer | outage 때 어떤 기능을 닫는가 | sequencer down runbook |
| finality | 금액별 완료 기준이 다른가 | confirmation policy matrix |
| UX | stuck 상태를 어떻게 보여주는가 | status copy와 support macro |
| ledger | soft paid와 final settlement를 분리했는가 | payment state machine |
실무 예시
Confirmation policy matrix
| 결제 유형 | 최소 사용자 표시 | 내부 ledger 상태 | 추가 확인 |
|---|---|---|---|
| $5 API call | Processing → Paid | PaidSoft | abuse limit과 rate limit |
| $200 checkout | Processing → Paid | PaidFinal | L2 finalized + transfer event |
| $20,000 merchant payout | Delayed 가능성 표시 | SettlementPending | L1 posted 또는 proof status |
| Treasury rebalance | 진행률 상세 표시 | BridgePending | bridge final 또는 destination mint |
운영Sequencer down runbook
| 단계 | 행동 |
|---|---|
| Detect | L2BEAT status, RPC error rate, pending tx queue, sequencer health를 확인한다 |
| Contain | 신규 checkout route를 Limited 또는 Disabled로 바꾼다 |
| Protect users | submitted 결제는 Delayed로 표시하고 중복 결제를 막는다 |
| Recover | relayer nonce, L1 force path, refund queue를 확인한다 |
| Communicate | status page와 merchant dashboard에 영향 범위를 적는다 |
| Review | route disable threshold와 confirmation policy를 업데이트한다 |
흔한 오해와 실패 시나리오
| 오해 | 실제 기준 |
|---|---|
| explorer에 보이면 결제가 끝났다 | explorer 표시와 final settlement는 다르다 |
| sequencer confirmation은 finality다 | sequencer accepted는 UX 신호이고 회계 확정은 별도 기준이 필요하다 |
| DA는 protocol researcher만 볼 내용이다 | DA는 user withdrawal, fraud proof, audit trail과 직접 연결된다 |
| stuck 상태는 error로만 처리하면 된다 | 정상 지연, retryable, manual review, refund 필요 상태를 분리해야 한다 |
| bridge finality와 L2 finality는 같다 | cross-chain settlement는 source와 destination finality를 모두 본다 |
실습 과제
- 백엔드Confirmation policy matrix 작성: low-value API payment, normal checkout, high-value merchant settlement, treasury withdrawal에 대해 wallet displayed, sequencer accepted, L2 finalized, L1 posted, bridge final 중 어떤 기준을 쓸지 정한다.
- 운영Sequencer down runbook 작성: sequencer 장애 시 checkout 접수, relayer retry, refund queue, status page, support macro를 어떤 순서로 처리할지 runbook으로 작성한다.
- 클라이언트[OPS] Stuck state UX 설계: 정상 지연, provider 장애, 재시도 가능, 수동 검토, 환불 필요 상태를 구분해 사용자 표시 문구와 운영자 증거 링크를 설계한다.
완료 기준
- DA, retrievability, blob availability, archive access를 구분했다.
- 금액과 업무 유형별 confirmation policy를 작성했다.
- sequencer outage 때 닫을 기능과 유지할 기능을 정했다.
- stuck 상태를 포함한 payment state machine을 만들었다.
근거 자료
- Data Availability Sequencer Finality: 03-크로스체인-L2/03-Data-Availability-Sequencer-Finality.md
- Ethereum.org Data Availability: https://ethereum.org/developers/docs/data-availability/
- Ethereum.org Optimistic Rollups: https://ethereum.org/developers/docs/scaling/optimistic-rollups/
- L2BEAT Scaling Risk Overview: https://l2beat.com/scaling/risk