암호학

23.04.28 [ZK] R1CS

슈팅스타제제 2023. 4. 30. 21:44

zkSNARK 에 사용되는 R1CS 에 대해 이해한대로 정리해보았다. 

- what is zk-SNARK? 

zkSNARK 는 영지식증명 중에서도 비대화형으로 zcash 프로토콜에서 알고리즘으로 개발되어 사용되고 있다.

zk 가 나오게 된 배경, optimistic-rollup vs zk-rollup, zkVM vs zkEVM, zk-SNARK vs zk-STARK 등의 개념은 따로 정리해야겠다.

 

- what is R1CS?

영지식증명에서는 증명에 사용되는 복잡한 computational statements 를 산술 회로로 모델링하여 계산하는 과정이 필요하다.

따라서, 연산 과정을 최대한 단순화하여 모델링하기에 용이한 시스템으로 이해했다.

뒤에서 설명할 예시는 간단하지만 복잡한 시스템을 R1CS 로 모델링하기 위해서는

보통 circuit 으로 회로를 디자인하고 연산, 증명 및 결과 zkey 를 생성한다. 

iden3 에서 제공하는 SNARKjs 라이브러리가 있는데

이걸 사용하면 키 생성 및 연산 결과가 진짜 너무 간단하게 도출되어 생각보다 허무했다.

 

Rank 1 Constraint System

용어에 대해 살펴보자면, rank 1 행렬은 선형 독립 연산으로

행렬을 소거했을 때 만들어지는 피봇 행렬이 최대 1 인 행렬이며, 

constraint 는 회로 연산을 표현한 방정식이다. 

R1CS 에서는 3가지 연산 (덧셈, 곱셈, 뺄셈) 만 다루는데,

나눗셈, 모듈러 연산 같은 경우에는 덧셈, 곱셈으로 다시 나타낼 수 있기 때문이다. 

 

- example of R1CS

다음과 같은 임의의 해시 함수가 있다고 할 때,

증명자는 검증자에게 해당 해시 함수를 알고 있다는 사실을 증명하기 위해 4를 대입한 값인 72 를 output 으로 제출할 수 있다. 

이때, 검증자는 증명자가 4 라는 값을 사용하여 연산했다는 사실을 모르며, 

72 라는 output 과 연산에 사용된 중간값만 가지고도 증명자가 h(x) 에 대한 연산을 했다는 사실을 검증할 수 있다. 

 

계산 과정을 말로 풀어보자면

h(x) 함수의 변수 x 에 대한 차수를 2차로 내리기 위해 

먼저 곱셈 연산으로 이루어진 식 x * x 를 변수 sym1 으로 치환한다. 

그리고 2차로 내려진 식을 다시 1차로 내리기 위해 x * sym1 을 y 로 치환한다. 

그리고 치환한 y - x 를 sym2 로 치환하면 output 에 대한 연산은 치환한 변수를 사용하여

output = sym2 + 7 로 나타낼 수 있다.

 

위 식을 곱셈 연산 (c=a*b) 의 형태로 나타낼 수 있는데 (1, x, output, sym1, y, sym2 순으로)

먼저, sym1 = x * x 같은 경우에는  a = [0, 1, 0, 0, 0, 0] b = [0, 1, 0, 0, 0, 0] c=[0, 0, 0, 1, 0 , 0] 

y = x * sym1 은 a = [0, 1, 0, 0, 0, 0] b = [0, 0, 0, 1, 0 , 0] c = [0, 0, 0, 0, 1, 0]

sym2 = y - x 는 (y-x)*1 로 나타낼 수 있기 때문에 a = [0, -1, 0, 0, 1, 0] b =[1, 0, 0, 0, 0, 0] c = [0, 0, 0, 0, 0, 1]

output = sym2 + 7 도 (sym2 + 7)* 1로 나타낼 수 있기 때문에 a = [7, 0, 0, 0, 0, 1] b = [1, 0, 0, 0, 0, 0] c = [0, 0, 1, 0, 0, 0] 

 

그르면 이제 1차로 내린 행렬 a 들에 대한 행렬 A, b 들에 대한 행렬 B, c 들에 대한 행렬 C 를 만들 수 있다. 

그리고 c = a*b 즉, a*b-c = 0 이며 이를 A, B, C 행렬에 대한 스칼라곱 연산으로 변환할 수 있다. 

아래 수식에서 S 는 곱셈 연산으로 나타내기 위해 지정한 1, x, output, sym1, y, sym2 에 대한 basis 행렬이다. 

행렬 S 의 output 가 7 일 경우, 아래 식을 만족하는 행렬 S 를 계산함으로써 증명이 맞는지 검증할 수 있다. 

행렬 C 를 이항하는 이유는 R1CS 는 영벡터를 포함해야 하기 때문인가?

- circuit of zk-SNARK

위 연산을 Circom 의 circuit 으로 회로 게이트 연산을 작성 및 모듈화할 수 있다. 

 

// circuit.circom 

template BadHash() {
	signal private input x;
	signal x_squared;
	siganl x_cubed; 
	signal output out; 

	x_squared <== x * x; 
	x_cubed <== x_squared * xl 
	out <== x_cubed - x * 7;
}

component main = BadHash();

 

증명자가 계산하는데 사용한 중간 값에 대한 변수는 회로 연산에서 신호 signal 로 나타낼 수 있다. (circom 에서 다루는 변수 타입인 듯)

증명자가 사용한 값 x 인풋값은 검증자에게 알리면 안되므로 private 으로 정의한다. 

 

circom 에서 다루는 연산 기호에 대해 간단히 살펴보자면, 

<+ 는 signal 변수에 대한 초기값을 할당하는 역할을 한다. 

 js 의 = 대입 연산자랑 비슷한 역할을 하나? 생각했었는데

값 할당 외에도 추가적인 constraints 를 설정할 수 있는 역할이 있다고 한다. 

그때 이 연산자가 어떻게 작동하는지는 잘 모르겠다. 그리고 초기값으로 설정된 해당 신호의 R1CS 는 a = 3 이 아니라 a - 3 = 0 이다. 

a <+ 3;

그리고 <== 연산 같은 경우는 할당은 할당인데 조건부 할당이 가능한 연산자이다. 

x_squared <== x * x;

GPT 에 따르면, , a <== (b < c ? 1 : 0)b < c라는 조건이 참이면 a1을, 거짓이면 a0을 할당 한다고 한다. 

 

- 적고보니 드는 의문

1. 행렬 S 에서 output 값으로 인해 x 값이 도출되는 것은 증명자만 알고 있는 사실인가?

이것이 로컬(레이어2)에서 계산되어 레이어 1으로 올라가는건가?

2. SNARK js 에서 증명 연산 결과와 함께 생성되는 증명키, 확인키에는 어떤 값으로 계산되는 것인지 궁금