안녕하세요, BQ개발자입니다.
ZK 개발을 시작하려는 분들을 위한 시리즈를 시작합니다.
첫 주제는 유한체입니다.
Circom이든 Noir이든, 모든 ZK 언어는 유한체 위에서만 동작합니다.
일반 정수가 아니라요. 이걸 모르면 내 코드가 왜 이상하게 동작하는지 이해할 수 없습니다.
❓ 유한체란?
컴퓨터는 숫자를 저장할 때 정해진 칸을 사용합니다. uint256은 256개의 0과 1로 숫자를 표현하는데,
가장 큰 수는 115792089237316195423570985008687907853269984665640564039457584007913129639935입니다. 78자리 숫자죠.
이 숫자에 1을 더하면 어떻게 될까요? 257번째 자리가 필요한데, 그런 공간이 없습니다.
그래서 모든 자리가 0으로 리셋됩니다. 실제로 100억 원이 있던 토큰 잔액이 1원을 더 받는 순간 0원이 되어버린 해킹 사고가 있었습니다.
유한체는 이 "최댓값 다음이 0"이라는 현상을 아예 정상 동작으로 만들어버린 시스템입니다. 버그가 아니라 원래 그렇게 설계한 거죠.
솔리디티 개발자라면 늘 조심하는 상황이 있습니다.
uint256 최댓값에 1을 더하면 0이 되기 때문이죠.
다시 말해 256비트로 표현할 수 있는 한계를 넘어서면, 다시 0으로 돌아가는 상황을 겪게되고,
이게 정수 오버플로우입니다.
TimeLock 해킹이 바로 이걸 악용했던 사례죠
미래 시점의 잠금에 큰 수를 더해서 0으로 만들어버린 다음
즉시 출금이 가능하도록 해서 해킹이 가능했습니다..ㄷㄷ
이 문제를 해결하려고 개발자들은 계속 방어 코드를 추가해왔습니다.
SafeMath 라이브러리를 쓰거나, Solidity 0.8.0부터는 컴파일러가 자동으로 체크하죠
하지만 이건 비용입니다. 모든 연산마다 "오버플로우 체크"가 추가로 실행되니까요.
문제의 핵심은 이거죠.
일반 정수 시스템에서 오버플로우는 예외적 오류라는점
그래서 막아야 하고, 막는 데 비용이 듭니다.
ZK에서 사용하는 유한체 Fp를 보겠습니다.
0부터 p-1까지의 정수로 이루어져 있고, 모든 연산이 mod p로 정의됩니다.
p-1에 1을 더하면? 0이 됩니다.
이건 오류가 아니라 정의죠.
유한체는 이렇게 처음부터 "순환하는 시스템"으로 설계되었습니다.
p를 넘으면 다시 0부터 시작하는 게 정상 동작이죠.
차이를 정확히 짚어보겠습니다.
일반 정수에서 오버플로우는 보통 예외상황으로 봅니다.
대부분의 연산은 정상 범위에서 일어나고, 가끔 경계를 넘을 때만 문제가 생기죠.
그래서 매번 체크해야 합니다.
하지만 유한체에서는 순환이 정상입니다.
애초에 모든 연산이 mod p로 정의되어 있으니, 체크할 필요가 없습니다.
오버플로우라는 개념 자체가 없는 거죠.
Circom에서 그냥 `sum <== a + b`라고 쓸 수 있는 이유입니다.
결과가 p를 넘든 안 넘든, 항상 유효한 값이 나오니까요.
✏️ 버그가 사라진 세계
유한체를 처음 접했을 때 깨달은 점이 있습니다.
우리가 "오버플로우 버그"라고 부르던 것은, 사실 무한을 유한한 공간에 억지로 담으려다 생긴 문제였다는점!
당연하게도 메모리는 유한한데 거기에 무한하게 큰 수를 담을 수 없으니까요.
유한체는 처음부터 유한하다고 가정합니다.
그래서 모든 숫자가 0부터 p-1 사이에 있고, 모든 연산 결과도 그 안에 있도록 하는거죠
무한을 다루려는 시도 자체가 없으니, 오버플로우도 없습니다.
ZK 회로를 작성할 때 SafeMath 같은 게 필요 없는 이유가 여기 있습니다.
오버플로우 걱정 자체를 할 필요가 없으니까요.
다음 편에서는 "음수가 없는데 뺄셈은 어떻게?"를 다뤄보겠습니다.
이것도 유한체의 독특한 접근 방식이 있더라구요!!
---
여러분의 좋아요와 공유는 사랑입니다 🥰
#BQ개발자 #ZK개발 #유한체 #Circom #영지식증명
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7