일반적인 상태 전이 과정에서는 모든 참여자가 이전 상태를 검증해야 하는데, zk-Rollup에서는 ZKP를 통해 직접 실행하지 않고도 모든 트랜잭션이 제대로 수행되었음을 보장할 수 있다.
==즉 모든 transaction이 제대로 수행되었다는 validity correctness를 보장해준다==
zk-Rollup은 새로운 상태를 모르는 상태에서는 새로운 트랜잭션을 생성할 수 없기 때문에, 데이터 가용성(Data Availability)을 보장한다
==zkp를 이용해서 data availability도 보장해준다==
L2는 기본적으로 L1의 스마트 컨트랙트의 형태이다.
이 스마트 컨트랙트는 다음을 포함한다.
calldata(calldata = minimal representation of state transition = diff)
calldata(representation of state transition)가 없으면 rollup이라고 볼 수 없다.
왜냐하면 recontruction을 할 수 없기 때문이다.
=> rollup은 L1의 보안을 상속한다(여기서 보안은 validity와 DA를 의미)
=> forced tx을 L1 smart contract에 추가한다
forced tx는 유저가 zkp를 생성하는 coordinator에 tx를 보내는 것이 아니라, L1에 보내는 것이다
그래서 smart contract의 로직은
forced transaction을 수행하지 않으면 state transition을 수행하지 못할 것이다'가 된다
coordinator가 L1에 블록을 찍고 싶으면, forced tx을 include해야한다.
=> L1에 tx cost를 내야한다
=> 그런데 L1 tx cost는 비싸고, 그럴거면 L1을 쓰는 이유가 사라진다 (defeat whole purpose)
nuance : user가 fored transaction을 모아서 제출해서 최대한 L1의 비용을 L2의 비용으로 맞춘다.
=> 이렇게 하면 malicious cooridinator block producer로 인해 L2에 내 자산이 갇히지 않고 censorship resistant할 수 있다.
=> single도 가능하긴 한데 다운되는 상황도 있으니 필요하긴 하다 (얼마나 많은지는 trade-off일듯하다)
-> 그러면 L1에 transaction 구조를 봐보자
L1 : to(256bits), from(256bits), amount(256bits), sig(512bits), nonce
op : to(32bits), from(32bits), amount(32bits), sig(512) (op는 to, from 크기를 reduce할 수 있음), nonce(L2에서는 없앨 수 있음)
zk : to(32bits), from(32bits), amount(32bits), zkp, nonce(L2에서는 없앨 수 있음)
L1은 사이즈를 줄일 필요가 없다.
사이즈를 줄인다면 사이즈를 줄이는 연산도 제대로 돌아가는지 남들이 돌려봐야하므로 cost가 든다.
zk rollup에서는 sig가 필요 없다
대신 zkp를 붙이는데 zkp는 하나의 sig보다는 크지만, 블록은 sig들이 쌓이기 때문에 결과적으로 더 싸진다.
op도 bls를 써서 sig 사이즈를 줄일 수 있다
-> technically 맞다
자세히 살펴볼 필요가 있다.
방법 1 : 트랜잭션 생길때마다 new leaf를 추가함 -> old leaf는 discard됨
방법 2 : nonce 사용하기 -> tx 할 때마다 하나씩 추가
그러나 nonce는 L2에서 없앨 수 있다.
왜냐하면 from이 있으면 어디서 왔는지 알 수 있으니까 nonce를 하나 올리면 되므로 같은 역할을 한다.
그래서 쓸 수는 있지만 inefficient하다
sharding은 많은 다른 사람들에게 execute different things를 시키는 것이 목적이었지만, 지금은 data availability를 얻기 위함
그럼 왜 zkrollup은 어려울까?
evm에서 처리할 때, logic이 dynamic하기 때문
단순 송금이라면 괜찮을 수 있지만,
smart contract를 배포한다던가 하면,
그걸 어떻게 zk로 leaf로 표현할건데 라는 어려움이 생긴다
dynamic하게 하는 수밖에 없어서 구현 어렵다(setup, proving keys..)
그럼 zkp로 바꿔주는 컴파일러는?
ethereum에는
stack, bytecode, mem, storage ... 등이 있는데
storage가 가장 zk로 바꾸기 어렵다
왜 그럴까?
-> hash function 때문에 비쌈, mpt가 implementation이 힘듦
storage도 zk로 바꾼다고 하면
어떤것이 바뀌었을때 trigger돼서 바뀌는것 다 추적해서 해야함
원래 ethereum에서는 모두가 연산을 했지만, zkp를 사용하면 모든 데이터를 다운로드하고 연산 돌릴 필요가 없다
bytecode측면에서 봤을때,
bytecode하나를 execute한 뒤 어디를 execute해야할 지 정해지지 않았다(dynamic)
zkp는 order가 있는데 어디로 갈지 모르면 어떻게 할것인가?
그리고 가더라도 뭘 할지 모른다(opcode가 다양해서)