~/blockchain-dev.md
BLOCKCHAIN_DEV

ZK proof가 맞아도 정산은 틀릴 수 있다: Aztec Connect 사고로 보는 L1/L2 경계

10분 읽기·2026.06.17·출처 5·00
오늘의 질문

롤업에서 “증명된 데이터”와 “L1에서 실제로 정산한 데이터”가 같은 범위라는 보장은 어디서 확인해야 할까?

blockchain-dev.md
SURVIVE.exe

ZK proof가 맞아도 정산은 틀릴 수 있다: Aztec Connect 사고로 보는 L1/L2 경계

1. 왜 지금 봐야 하나

2026년 6월 14일, 이미 deprecated 상태였던 Aztec Connect의 RollupProcessor 계열 컨트랙트에서 약 210만~219만 달러 규모의 자산이 빠져나간 사고가 보고됐다. SlowMist 분석은 영향을 받은 컨트랙트를 0xff1f2b4adb9df6fc8eafecdcbf96a2b351680455로 특정하고, 공격자가 numRealTxsdecoded_slots 사이의 경계 차이를 이용해 L1 풀의 자산을 인출했다고 설명한다.

이 사고를 “오래된 DeFi가 또 해킹당했다”로만 보면 학습 가치가 작다. 개발자에게 더 중요한 질문은 이것이다.

ZK proof가 어떤 public input 묶음을 커밋했다면, L1 settlement contract도 정확히 같은 묶음을 검증하고 정산하고 있는가?

롤업은 보통 “L2 실행을 압축하고 L1에서 검증한다”는 말로 설명된다. 하지만 실제 구현에서는 calldata decoder, proof verification, public input hash, settlement loop, deposit accounting, withdrawal accounting이 서로 다른 코드 경로에 놓인다. 이 경로들이 같은 범위를 같은 의미로 읽는다는 보장이 깨지면, proof 자체가 통과해도 L1/L2 상태가 갈라질 수 있다.

2. 핵심 개념

이번 글의 핵심 개념은 **정산 경계(settlement boundary)**다. 롤업에서 정산 경계란 “L2 상태에 반영되는 값이 L1 자산 회계에서도 검증·차감·입금 처리되는 정확한 범위”를 말한다.

단순화하면 롤업 정산은 세 층으로 나뉜다.

  1. 입력 해석: calldata에서 rollup batch, public input, proof 관련 값을 읽는다.
  2. 증명 검증: ZK proof 또는 public input hash가 특정 데이터에 대해 유효한지 확인한다.
  3. L1 회계 반영: deposit balance를 차감하거나 withdrawal을 지급하는 등 실제 L1 자산 상태를 바꾼다.

안전한 설계에서는 세 층이 같은 거래 집합을 바라봐야 한다. 예를 들어 proof가 32개의 public input slot을 커밋했다면, L1 회계도 그 32개 중 자산에 영향을 주는 모든 slot을 독립적으로 검사해야 한다. 반대로 L1 loop가 1개 slot만 처리한다면, 나머지 31개 slot은 proof 안에서도 자산 효과를 만들 수 없어야 한다.

SlowMist가 설명한 취약점의 요지는 이 불변식이 깨졌다는 것이다. 공격 시나리오에서 numRealTxs = 1, decoded_slots = 32 형태의 차이가 만들어졌고, ZK proof/SHA256 commitment 경로는 더 넓은 slot 범위를 포함했지만 L1 settlement loop는 더 좁은 범위만 순회했다. 그 결과 일부 slot은 L2 state에는 의미 있게 들어가지만 L1 회계 검증은 받지 않는 “그림자 입력”이 됐다.

3. 최신 이슈와 연결

SlowMist는 공격자가 31/32 public input slot이 ZK proof에 커밋되면서도 L1 settlement 검증을 받지 않는 구조적 gap을 만들었다고 분석했다. The Defiant와 Cointelegraph도 공통적으로 “proof verification path와 Ethereum settlement logic이 transaction list를 다르게 해석했다”는 점을 핵심 원인으로 정리했다.

여기서 중요한 표현은 “증명 검증 실패”가 아니라 두 경로의 해석 불일치다. ZK 시스템에서 개발자는 종종 circuit correctness에만 시선을 빼앗긴다. 하지만 롤업 보안은 circuit만으로 끝나지 않는다. Solidity decoder가 읽은 길이, precompile에 넣은 byte range, public input hash에 들어간 slot, settlement loop의 upper bound, deposit balance를 차감하는 조건이 함께 하나의 상태 전이 규칙을 만든다.

또 하나의 최신성은 “deprecated contract”라는 운영 조건이다. Aztec 공식 문서는 Aztec Connect가 더 이상 active development 상태가 아니며, Aztec 운영 rollup instance가 2023년 3월 21일 deposit 수락을 중단했고 2024년 3월 31일 sequencer 운영을 중단한다고 안내했다. 포럼 공지도 2023년 3월 17일 같은 sunset 일정을 설명했다. 즉 사용자는 철수하라는 안내를 받았지만, 온체인 컨트랙트와 잔여 자산은 계속 남아 있었다.

이 조합이 무섭다. immutable contract는 “누구도 마음대로 바꿀 수 없다”는 장점이 있지만, 철수하지 않은 자금이 남은 상태에서는 “누구도 즉시 고칠 수 없다”는 단점도 된다.

4. 개발자 관점 해석

첫째, proof의 유효성과 회계의 완전성은 다른 속성이다. ZK proof가 어떤 statement를 증명했다는 말은 그 statement가 정확히 무엇인지에 의존한다. 만약 statement가 “이 byte range에 대한 hash가 맞다”에 가깝고, L1 회계가 “이 loop가 순회한 deposit만 인정한다”에 가깝다면, 두 statement 사이의 매핑을 별도로 증명해야 한다.

둘째, 길이 파라미터는 보안 파라미터다. numRealTxs, rollupSize, numInnerRollups, decoded_slots 같은 값은 단순 최적화나 포맷 값이 아니다. 누가 제공하고, 어느 범위로 clamp되고, 어떤 값끼리 같아야 하고, rounding 이후 gap이 생기는지를 property로 검증해야 한다. 공격자는 보통 “값 자체”보다 “두 값 사이의 관계”를 공격한다.

셋째, unused slot은 반드시 무해해야 한다. batch format에서 padding, noop, reserved slot, future extension field를 둘 때는 “사용하지 않는다”가 아니라 “사용돼도 자산 효과를 만들 수 없다”를 증명해야 한다. 사용하지 않는 slot의 publicValue가 0이어야 한다면, 그 제약은 circuit·contract·test 모두에서 확인돼야 한다.

넷째, sunset은 제품 공지가 아니라 보안 프로토콜이다. 문서에 “withdraw immediately”라고 쓰는 것만으로는 충분하지 않다. 운영 종료 후에도 잔여 자산이 있는 contract는 공격면으로 남는다. pause/admin key를 제거한 뒤에는 대응 옵션이 급격히 줄어든다. 탈중앙성과 무관리성의 장점을 택했다면, 그만큼 사전 철수, 잔액 모니터링, 위험 공지, escape path 테스트가 더 중요해진다.

다섯째, 감사는 배포 시점뿐 아니라 변경 시점에도 필요하다. 보도에 따르면 RollupProcessorV3 관련 변경은 sunset 이후에도 있었고, 외부 감사 없이 진행됐다는 지적이 있었다. 사용량이 줄어든 시스템일수록 “작은 변경”이 오래 방치된 자금을 건드릴 수 있다.

5. 내 프로젝트에 적용할 체크포인트

롤업, 브리지, cross-chain messaging, account abstraction bundler, batch settlement 시스템을 만든다면 아래 질문을 코드 리뷰 체크리스트로 쓰자.

  • 같은 calldata를 읽는 경로가 둘 이상 있는가? decoder, verifier, executor가 같은 길이와 순서를 보장하는가?
  • numItems, realItems, paddedItems, maxItems, roundedItems 같은 변수가 서로 어떤 관계여야 하는지 주석이 아니라 assertion으로 남아 있는가?
  • proof가 커밋한 public input 범위와 contract가 실제 회계 처리하는 범위가 정확히 일치하는가?
  • padding/noop/reserved slot이 asset delta, owner, recipient, fee, nonce에 영향을 줄 수 없는가?
  • L1에서 검증하지 않는 값이 L2 state root에는 들어갈 수 있는가? 들어간다면 왜 안전한가?
  • batch 크기 변경, circuit key 변경, decoder 변경, gas 최적화가 settlement invariant를 깨뜨리지 않는지 property test가 있는가?
  • deprecated 또는 maintenance mode contract에 잔여 자산이 있는지 대시보드로 보고 있는가?
  • admin key 제거, pause 제거, upgradeability 포기 전에 “남은 자금이 0에 가까운가”와 “비상 withdrawal 경로가 실제로 동작하는가”를 확인했는가?

6. 오늘 10분 액션

오늘 10분 동안 내 코드나 관심 프로토콜 문서를 하나 골라 다음 표를 채워보자.

항목확인할 것
입력 길이batch length, real tx count, padded count가 어디서 정해지는가
증명 범위proof/public input hash가 몇 개 항목을 커밋하는가
실행 범위settlement/execution loop가 몇 개 항목을 순회하는가
gap 처리proof에는 들어가지만 실행되지 않는 slot, 또는 실행되지만 proof에 없는 slot이 있는가
무해성unused slot의 value/owner/token/recipient가 0 또는 sentinel로 강제되는가
테스트길이 불일치 fuzz/property test가 있는가
운영 종료잔여 자산, pause, upgrade, withdrawal 안내 상태는 어떤가

시간이 남으면 Foundry나 Hardhat 테스트에 이런 property 이름만 먼저 추가해도 좋다.

zsh — 생존확인.sh
invariant_publicInputsCommitted_equal_settlementInputsProcessed
invariant_paddingSlots_cannotCreateAssetDelta
invariant_numRealTxs_cannotBeLessThanValueBearingSlots

테스트 본문을 오늘 다 쓰지 않아도 된다. 중요한 것은 시스템의 안전 조건을 “느낌”이 아니라 이름 붙은 invariant로 바꾸는 것이다.

7. 더 볼 자료

  • SlowMist의 기술 분석은 numRealTxs, decoded_slots, slot gap, attack transaction, gas used 등 구현 레벨 단서를 제공한다.
  • Aztec Connect Sunset 문서는 운영 종료가 어떻게 안내됐고, sequencer 중단 이후 사용자가 어떤 책임을 갖게 되는지 보여준다.
  • Aztec 포럼의 2023년 sunset 공지는 “운영자가 멈춘 뒤에도 누구나 rollup instance를 돌릴 수 있다”는 open-source/liveness 가정을 확인하는 데 도움이 된다.
  • The Defiant와 Cointelegraph 보도는 이번 사고가 current Aztec network와 분리된 legacy product 이슈였다는 맥락, 그리고 immutable/deprecated contract의 대응 한계를 요약한다.

중복 회피 메모

최근 blockchain_dev 글은 OP Stack interop의 cross-chain safe head, Uniswap v4 hook의 callback 실행 경계, Ethereum gas/block size limit, ProxyAdmin 권한 사고, ZK Gateway settlement layer, FOCIL inclusion list, PeerDAS data availability를 다뤘다. 이번 글은 특정 브리지 운영이나 컨트랙트 권한이 아니라 ZK rollup에서 proof commitment 범위와 L1 settlement loop 범위가 어긋날 때 생기는 L1/L2 state discrepancy에 집중한다. 또한 deprecated immutable contract의 잔여 자산 리스크를 “sunset도 보안 프로토콜”이라는 관점으로 해석해 기존 글과 구분했다.

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)