블록체인 확장성 솔루션 시리즈 1–2 :: Casper FFG — Q&A / Code Review

in #ethereum6 years ago (edited)

Geon-gi Mun(@moongunflag)
Seoul Nat’l Univ. Blockchain Academy Decipher(@decipher-media)

*Note: 이 글은 서울대학교 블록체인 학회 디사이퍼에서 작성한 미디엄 아티클을 옮긴 글입니다. 이 글에서 설명하는 모든 내용은 2018년 4월 17일을 기준으로 합니다.


서울대학교 블록체인 학회인 ‘디사이퍼(Decipher)’에서 블록체인의 스케일링 솔루션에 관한 글들을 시리즈로 연재합니다. 시리즈의 첫 번째 주인공은 Ethereum의 “Casper the Freindly Finality Gadget”입니다. 1–1. Casper FFG Overview, 1-2. Casper FFG — Q&A 및 Code Review로 나누어 설명합니다.

지금까지 Casper FFG(이하 캐스퍼) 논문을 바탕으로, 캐스퍼란 무엇인가?에 대해 그 개념을 이해해보았다. 이번 글에서는 논문을 읽고 난 후에 생각을 확장해보며 생겼던 궁금증들을 Q&A 형태로 공유해보고자 한다. 또한, 현재 이더리움 Github에는 이더리움의 새로운 언어인 Vyper로 캐스퍼 컨트랙트를 구현해둔 코드가 있다. 구현된 코드를 보면 이해하기 어렵던 개념이 명쾌하게 이해되는 경우가 많으니, 이를 한 번 살펴보며 캐스퍼를 좀 더 자세히 이해해보도록 하자.

[Casper FFG에 대한 궁금증 Q&A]

1. 캐스퍼 프로토콜의 규칙2(NO_SURROND)는 어떤 의미를 함축하고 있을까?

캐스퍼 프로토콜에는 2가지의 규칙이 존재한다. 이 중 규칙2는 선후관계에 상관없이 내가 2가지의 투표 메시지 — (v1, s1, t1, h(s1), h(t1)), (v2, s2, t2, h(s2), h(t2)) — 를 제출하였다고 가정했을 때, h(s1) < h(s2) < h(t2) < h(t1) 이어서는 안 된다는 규칙이다. 규칙1에 비해 그 의미를 직관적으로 이해하기 어렵다. 캐스퍼 프로토콜에서 이 규칙이 무슨 역할을 하고 있는지 살펴보자.

먼저, 논문에서 증명된 캐스퍼의 2가지 속성 중에서 ‘충돌(conflict)하는 서로 다른 체크포인트가 동시에 finalize되기 위해서는 검증인 집단의 예치금 ⅓ 이상이 몰수되어야 한다’는 Accountable Safety 증명 과정을 살펴보면, 규칙2를 이해해볼 수 있다. 캐스퍼에서 하나의 체크포인트(a_m)가 finalize되기 위해서는 반드시 a_m와 그 직계 자손인 a(m+1)이 모두 justify되어야 하는데, 이 때 Safety 속성이 지켜지기 위해서는 a_m와 충돌하는 다른 체크포인트(b_n)가 finalize되어서는 안 된다. 먼저 프로토콜 규칙1이 존재하기 때문에, a_m와 같은 높이인 다른 체크포인트를 향한 투표가 ⅓ 이하일 것이므로, h(a_m)=h(b_n)인 경우는 없을 것이다. 하지만 a_m와 다른 높이이면서 충돌하는 체크포인트들이 finalize되는 경우는 막을 수 있을까? 규칙1만으로는 막을 수 없다. 여기서 규칙2가 필요하다. 증명의 편의를 위해, h(a_m)<h(b_n)이고, 루트(r)에서부터 b_n을 향한 supermajority link들이 잇고 있는 체크포인트들이 r→b_1→b_2→…→b_n와 같이 존재한다고 해보자. 이 때 h(a(m+1))<h(b_j)를 만족하는 가장 작은 j번째 체크포인트를 보면, h(b(j-1))<h(a_m)을 만족한다. (그림1 참고) 다시 말해, a_m과 다른 높이이면서 충돌하는 체크포인트(b_n)가 finalize되려면, h(b(j-1))<h(a_m)<h(a(m+1))<h(b_j)_인 형태의 투표 메시지가 중간에 반드시 필요한 것이다. 따라서 이러한 형태의 투표 메시지를 금지한다면, a_m과 다른 높이이면서 충돌하는 체크포인트가 finalize되는 것을 막을 수 있고 이것이 프로토콜 규칙2의 목적이다.

그림 1. Accountable Safety 증명(출처: 캐스퍼 논문)

프로토콜의 Accountable Safety를 위해 규칙1과는 다른 새로운 규칙이 존재해야 한다는 것은 이해하였다. 그런데 이를 일반화하여 h(s1)<h(s2)<h(t2)<h(t1) 형태의 투표 메시지를 모두 금지하였더니 side case가 생겼다. 만약 s1, s2, s2, t1이 충돌하지 않는 하나의 branch에 속해 있다고 한다면, h(s1)<h(s2)<h(t2)<h(t1)이라고 한들 모두 justify될 수 있는 체크포인트들이 맞지 않는가? 이 경우는 네트워크에 피해를 주지 않는 경우임에도 불구하고, 규칙2는 예외없이 금지하고 있다. 이를 예외적으로 허용할 수 있겠으나, Vitalik Buterin은 이 규칙이 ‘검증인들이 잊어버리는 것을 금지하는 규칙’이라고 설명했다.이러한 설명으로 미루어보아, 규칙2가 검증인들의 비효율적인 투표 행위를 막고 있는 역할도 할 수 있으므로 굳이 이 경우를 예외적으로 허용하는 선택을 하지 않은 듯하다.

나아가 이 규칙은 검증인들이 source와 target의 간격이 짧은 투표를 하도록 유도하는 역할도 간접적으로 수행할 수 있다고 생각한다. 왜냐하면 검증인들은 자신이 했던 모든 투표를 기억하지 못할 수 있으므로 규칙2를 어기는 risk를 피해 h(t)-h(s)<=2인 투표를 하려고 할 것이기 때문이다. 체크포인트들의 finalize를 위해서는 h(t)-h(s)=1인 투표가 필요한 프로토콜에게 도움이 되는 유인이다.

2. 캐스퍼를 통해 좋아지는 부분은 무엇이며, 캐스퍼로 갔을 때 발생할 수 있는 문제점은 무엇이 있을까?

PoW 51% attack의 위협이 낮아진다.

캐스퍼를 도입하게 되면 매 50번째 블록마다 원본 체인이 무엇인가에 대한 단 하나의 비가역적인 답이 정해질 수 있다.(해당 체크포인트가 finalize된다는 가정 하에) 더불어 fork choice rule로 인하여 한 번 finalize된 체크포인트는 이후에 어떤 경우에도 취소되지 않는다. 그렇기 때문에 만약 PoW 마이닝 파워의 51% 이상을 가진 세력이 등장하여 위변조한 체인을 더 길게 만들 수 있다고 하더라도, 이미 finalize된 체크포인트를 무효화하고 원본으로 채택될 수 없다. 따라서 이들의 공격은 새로운 체크포인트가 finalize되기 전까지 가장 최근의 블록들만을 변조하는 데에 그칠 것이므로 그 위협이 줄어든다.

하지만 새로운 공격들도 가능해진다.

그렇다면 캐스퍼가 도입되면 아무런 문제가 없을까? 논문 마지막 파트에는 캐스퍼가 도입됐을 때 실현 가능한 2개의 공격을 소개하고 있다. 각각 Long Range Revisions와 Catastrophic Crash로 불리는 이 2가지 공격은 모두 새롭게 가능해진 공격들이다.

Long Range Revisions 공격은 PoW의 51% 공격과 그 양상이 비슷하다. 말 그대로 아주 오래 전으로 돌아가 모든 체크포인트를 새롭게 finalize하는 공격이다. 이 공격은 오랜 기간동안 총 예치금의 ⅔ 이상을 보유한 집단이 있고 이들이 현재는 모두 검증인 집단에서 탈퇴하여 예치금을 인출하고 난 이후의 상황을 가정한다. 이들은 오래 전의 체크포인트로 돌아가면 당시에는 총 예치금의 ⅔ 이상을 보유하고 있었으므로 계속해서 투표를 성사시켜 나갈 수 있는데, 이미 예치금을 인출했으므로 예치금 몰수에 대해서도 두려워하지 않을 것이다. 이 공격에 대해 캐스퍼는 항상 최초로 finalize된 블록을 선택하며 이후에 finalize된 블록이 생기더라도 모두 무시하는 규칙(fork choice rule)과, 검증인들이 예치금을 인출하고자 할 때 의도적인 인출 지연 시간(withdrawal delay)을 도입하여 해결하고자 한다. 이 때, 인출 지연 시간은 네트워크의 노드들이 finalize된 블록에 대한 정보를 업데이트하는 시간에 대한 기대값과 같다. 다시 말해 한 번 finalize된 체크포인트는 절대 되돌릴 수 없으므로, 이 체크포인트를 다수의 노드들이 알고 있기만 한다면 Long Range Revisions 공격은 막을 수 있다는 것이다.

Catastrophic Crash 공격은 검증인 집단 중 일부가 동시에 연결이 끊겼을 때 생기는 문제 상황을 말한다. 만약 검증인 집단 중에 총 예치금의 ⅓ 이상을 차지하고 있는 검증인들이 동시에 연결에 실패하게 되면, 그 때부터 어떤 체크포인트도 ⅔ 이상의 투표를 받을 수 없게 될 것이므로 어떤 체크포인트도 justify나 finalize될 수 없기 때문이다. 이를 해결하기 위해 캐스퍼는 ‘inactivity leak’이라는 개념을 도입하였다. 만약 투표를 하지 않는 검증인들이 존재한다면 시간이 지날 때마다 지속적으로 이들의 예치금을 깎는다는 개념이다. 이러한 장치가 존재한다면 투표에 참여한 검증인들의 예치금 합이 처음에는 총 예치금의 ⅔ 이상을 차지하지 못했다고 하더라도, 시간이 흐르면서 총 예치금의 ⅔ 이상을 차지할 수 있을 것이며 비로소 투표가 성사될 수 있기 때문이다. inactivity leak을 통해 회수한 예치금을 소각할 것인지 혹은 일정 시간이 지난 후에 다시 검증인에게 돌려줄 것인지는 아직 정해지지 않았다.

3. 캐스퍼를 도입했을 때 이더리움의 TPS가 빨라질 수 있을까?

캐스퍼에 대해 사람들이 가장 궁금해하는 질문은 결국 ‘결국 이더리움의 TPS가 빨라질 것인가?’이다. 우선 Casper FFG 기준으로는, 캐스퍼가 도입되더라도 블록을 생성하는 규칙(block proposal mechanism)은 현행 PoW로 유지되기 때문에 아쉽게도 TPS에는 변화가 없을 것이다. 하지만 Casper FFG 이후에 Casper CBC와 같이 블록 생성마저도 PoS로 전환하는 프로토콜이 적용된다면, TPS가 극적으로 개선될 수 있을 것이다. 현재 PoW 아래에서 블록 생성 주기를 일정 수준 이하로 낮추지 못하는 이유는, 작업 증명 난이도를 낮출수록 블록체인 전체의 비효율성이 증대되기 때문이다. 왜냐하면 작업 증명이 쉬워질수록 동시에 여러 채굴자들이 블록을 만들어낼 확률이 높아질 것이며, 이는 곧 체인에서 잦은 분기와 고아 블록 형성으로 이어져 원본 체인이 무엇인가에 대한 합의 비용을 높이기 때문이다. 블록 생성 규칙이 PoS로 전환되면 이러한 문제가 해결되어 TPS가 빨라질 수 있다.

4. 아직 해결되지 않은 Open Problem은 어떤 것들이 있을까?

Catastrophic crash 공격이 Inactivity leak으로 완전히 해결되지 않는다.

Inactivity leak을 적용하면 Catastrophic crash 공격이 어느 정도 해결되지만, 부작용이 있다. 어느 누구의 예치금도 몰수되지 않은 채로 충돌하는 서로 다른 체크포인트가 finalize되는 다음과 같은 상황이 나타날 수 있다.(그림 2 참고) 검증인 집단이 총 예치금의 ⅔ 이상을 차지하는 A라는 그룹과 나머지 예치금을 차지하는 B라는 그룹으로 이루어져 있고, A라는 그룹이 여러 개로 분기된 체인 중 한 쪽에만 투표하여 finalize를 시키고 난 뒤 전부 연결이 끊겼다고 가정해보자. 이 때 inactivity leak으로 인하여 나머지 한 쪽 체인에서는 A의 예치금이 지속적으로 소실될 것이고, 결국 남은 검증인 집단인 B 그룹에 의해 이 체인마저 finalize가 가능해질 것이다. 다시 말해 충돌하는 서로 다른 체크포인트가 모두 finalize되었으나 그 누구도 프로토콜 규칙을 어기지 않았으므로 예치금이 몰수되지 않는다. 이 경우에 대한 해결책은 아직 나오지 않은 상태다(2018.04 기준). 혹시 이 부분에 대해 관심이 있거나 좋은 의견이 있다면 이더리움 재단 리서치 채널을 참고하면 된다.

그림 2. Inactivity leak(출처: 캐스퍼 논문)

경제적인 incentive 구조 설계가 필요하다.

또한, 검증인들의 투표 참여를 독려하고 악의적인 행동을 막기 위한 경제 요인에 대한 설계가 아직 미완의 상태로 남아있다. 개념적인 틀은 잡혔으나 penalty와 reward에 대한 정교한 수치 설계가 남아있다. 이에 대해서는 Casper FFG의 경제 요인에 대한 논문이나 이더리움 재단 리서치 채널을 참고하면 된다.

[Code Review — 실제 구현된 코드 살펴보기]

이더리움 깃헙에 들어가보면, 500줄 정도의 길지 않은 코드캐스퍼 컨트랙트(Casper Contract)가 구현되어 있다. 코드를 하나하나 살펴보면서 캐스퍼를 좀 더 명확히 이해해보도록 하자. 코드는 크게 deposit, logout, withdraw, vote, slash로 나뉘어져 있다. 각 파트가 구현하고 있는 상황은 다음과 같다.

  • deposit : 검증인이 검증인 집단에 들어가 투표하기 위해 예치금을 캐스퍼 컨트랙트로 보낼 때
  • logout : 검증인 집단에서 투표하던 검증인이 더이상 투표를 그만두고 검증인 집단에서 탈퇴하고자 할 때
  • withdraw : 검증인 집단에서 탈퇴한 검증인이 예치해두었던 예치금을 인출하고자 할 때
  • vote : 검증인 집단에 포함된 검증인이 특정 체크포인트들에 대한 투표 메시지를 캐스퍼 컨트랙트로 보낼 때
  • slash : 검증인이 캐스퍼 프로토콜의 규칙을 어긴 것이 발각되어 예치금을 몰수당할 때

파트별로 코드를 쪼개서 설명할 것이며, 문맥 이해에 필요한 코드를 제외한 줄들은 생략하였다. 또한 기초적인 코딩 지식만을 지닌 사람이 전체 코드를 쉽게 이해할 수 있도록 돕는 데에 중점을 두었으며, Vyper 문법과 유사한 파이썬 코드를 읽을 줄 아는 사람이라면 직접 깃헙의 코드를 읽는 것이 효율적이라 생각된다. 더불어 현재(2018/04/16) 기준 가장 마지막으로 merge된 코드를 기반으로 분석하였음을 알린다.

1. deposit

우선 deposit을 하기 위해서는 캐스퍼 컨트랙트로 자신의 검증 주소(validation_addr)와 이후에 예치금을 다시 돌려 받을 주소(withdrawal_addr)를 함께 넣어서 보내야 한다. (이보다 앞서 이더리움 주소를 바탕으로 한 검증 코드 컨트랙트(validation code contract)를 만들어야 하는데, 이 검증 코드 컨트랙트의 주소가 validation_addr에 해당한다. 캐스퍼 컨트랙트 바깥에서 일어나는 상황이므로 자세히 살펴보지는 않겠다. 이에 대해서는 여기를 참고하는 것이 좋다.) 또한 보낸 메시지(msg)에 담긴 예치금(msg.value)이 최소 보증 금액(min_deposit_size) 이상이어야 한다(281, 285). (2018.04 현재 최소 보증 금액은 1,500ETH이다.) 예치금이 최소 보증 금액 이상임이 확인되면 현재 dynasty에 2를 더하여 start_dynasty를 계산한다.(286) (검증인은 추후에 이 start_dynasty부터 검증인 집단에 포함되어 투표권을 행사할 수 있게 된다.) 그리고 나서 메시지에 담긴 예치금(msg.value)을 deposit_scale_factor로 나누어 scaled_deposit에 저장해두는데(287), 이는 캐스퍼 논문에서 언급되지 않았던 부분이다. 현재 구현된 캐스퍼 코드 안에서 예치금은 항상 특정한 변수(deposit_scale_factor)로 나눈 상태로 저장되어 있다가, 검증인에게 예치금을 돌려줄 때에 다시 똑같은 변수(deposit_scale_factor)를 곱하여 실제 금액으로 환산되는 방식으로 다뤄지고 있다. 이에 대해 Vitalik Buterin이 언급한 부분1, 부분2를 참고하면 그 이유는 이러하다. 투표에 참가하지 않는 검증인들에게 inactivity leak을 적용할 것인데, 투표가 지속적으로 성사가 되지 않으면 이 factor를 이용해 해당 epoch의 모든 검증인의 예치금 규모를 동시에 손쉽게 줄이기 위함이다. start_dynastyscaled_deposit 계산이 끝나고 나면, 검증인 집단을 담고 있는 배열(validators[])의 새로운 검증인 번호(next_validator_index)에 이 검증인을 새롭게 추가한다(288). 이 배열(validators[])은 검증인 집단에 속하는 검증인들의 5가지 정보를 담고 있다 — 예치금(deposit), 검증인으로서 투표를 시작하는 시기(start_dynasty), 검증인 자격이 끝나는 시기(end_dynasty), 주소(addr), 예치금을 돌려줄 주소(withdrawal_addr). 이 때, 새롭게 검증인 집단에 추가될 때에는 아직 검증인 자격이 끝나는 시기를 알 수 없으므로 end_dynasty를 무한대로 설정해둔다(291). (default_end_dynasty에 무한대 값이 저장되어 있는데, 코드로는 10³⁰으로 구현되어 있다.) 검증인 집단에 새로운 검증인을 추가했다면, 검증인들의 번호가 저장된 배열(validator_indexes[])의 이 검증인의 출금 주소(withdrawal_addr)번 째 칸에 이 검증인의 번호(next_validator_index)를 저장한다(295). 추후에 해당 검증인의 출금 주소만으로 검증인 번호가 무엇인지 손쉽게 알 수 있도록 하기 위함이다. 다음 검증인 추가를 위해 새로운 검증인 번호(next_validator_index)를 1만큼 증가시킨다(296). 마지막으로 각 dynasty별로 직전 dynasty 대비 예치금 변화량을 담고 있는 배열(dynasty_wei_delta[])에서 검증인이 투표를 시작하는 시기(start_dynasty)에 예치금(scaled_deposit)만큼을 더한다(297). 캐스퍼에서는 매 dynasty가 진행될 때마다 예치금 총량의 ⅔ 이상이 투표했는지를 확인해야 하므로, dynasty마다 검증인 집단이 맡겨둔 예치금의 총량을 계산하는 것이 중요하다. 이 때, 직전 dynasty의 총 예치금에서 이 배열(dynasty_wei_delta[])에 저장된 예치금 변화량만큼만 더하거나 뺌으로써 총 예치금을 손쉽게 계산하기 위해 구현된 배열이다.

2. logout

먼저 검증인이 보낸 메시지(logout_msg)에 들어있는 정보들을 꺼내어 values에 담는다(308~311). 메시지에는 검증인 번호(validator_index), 메시지를 보낸 시기(epoch), 검증인의 서명(sig)이 들어있다. (RLP는 이더리움에서 쓰는 인코딩 형식이다.) 정보들을 꺼내고 나면, 현재 시기(current_epoch)가 검증인이 메시지를 보낸 시기(epoch)보다 이후임을 확인한다(312). 만약 메시지를 보낸 시기보다 현재 시기가 앞선다면 잘못된 메시지일 수 있기 때문이다. 그리고 나면 검증인이 검증인 집단에서 탈퇴하는 시기(end_dynasty)를 계산한다(316). 검증인이 탈퇴하는 시기는 현재 시기(dynasty)에 탈퇴 처리 지연 시기(dynasty_logout_delay)만큼을 더해서 계산한다. (검증인은 이 시기까지 검증을 하고 그 이후에 검증인 집단에서 탈퇴된다. 또한 논문에서는 logout_delay가 검증인으로서의 자격이 주어지는 delay와 마찬가지로 2로 가정하고 있지만, 코드에서는 상수가 아닌 변수로 처리되고 있다.) 계산된 end_dynasty와 검증자 배열에서 해당 검증자에게 저장되어 있는 end_dynasty를 비교하여 이전에 logout 처리된 검증인은 아닌지 확인한다(317). 이전에 한 번도 logout 처리하지 않았다면 end_dynasty가 default값인 무한대일 것이며, 이전에 logout 처리되었다면 당시에 end_dynasty가 계산되어 저장되어 있을 것이고 그 값은 현재 계산한 end_dynasty보다 작거나 같을 것이므로 이 비교를 통해 확인할 수 있다. (더불어 현재(2018.04)까지 구현된 바로는 한 번 logout한 검증인은 다시 검증인 집단에 들어올 수 없다.) 모든 확인이 완료되었다면, 검증인의 end_dynasty를 계산된 end_dynasty로 덮어씌워 저장한다(319). 마지막으로, end_dynasty 직후에 검증인이 탈퇴할 것이므로 그 때의 예치금 변화량에서 검증인의 예치금만큼을 뺀다(320).

3. withdraw

현재 dynasty가 검증인의 end_dynasty 이후인지 확인한다(340). 아직 end_dynasty가 지나지 않았다면 검증인이 검증인 집단에 포함되어 있다는 뜻이므로 출금을 처리해주면 안되기 때문이다. dynasty_start_epoch[]이란 매 dynasty가 시작된 epoch을 저장해두는 배열인데, 이 배열에서 end_dynasty+1의 값을 꺼내와 end_epoch에 넣는다(341). 다시 말해 end_dynasty 직후의 dynasty가 시작되는 epoch을 구한다. 이 epoch에 출금 지연 시간(withdrawal_delay)만큼을 더한 값보다 현재 epoch이 크거나 같은지 확인한다(341~342). 캐스퍼에서는 long range revision attack을 막기 위해 출금에 지연을 두는데, 출금을 신청하고 이만큼의 시간이 흘렀는지 확인하는 것이다. 자격이 모두 확인되면 출금을 진행한다. 검증인에게 저장되어 있는 예치금에다가 deposit할 때 나눴던 deposit_scale_factor로 다시 곱해서 인출 금액(withdraw_amout)를 계산한다(344). 계산한 인출 금액만큼을 검증인이 입력했던 돌려받을 주소로 송금한다(345). 마지막으로 검증인 집단 목록에서 해당 검증인을 삭제한다(348).

4. vote

투표 파트를 들어가기에 앞서, 구현된 코드에 캐스퍼 논문에는 없던 부분이 추가되어 있다. 논문에서는 체크포인트의 투표 마감 기한에 대한 언급이 없지만, 코드에서는 현재 epoch에 해당하는 체크포인트를 target으로 하는 투표 메시지만을 허용하는 조건이 추가되어 있다. 다시 말해 매 체크포인트에 대한 투표의 마감 기한을 해당 epoch으로 한정하고 있다는 것이다. 캐스퍼 논문 발표(2017.10)보다 구현된 코드(2018.04 현재 지속 수정중)가 최근 정보인 점을 감안했을 때, 새로이 추가된 조건인 것으로 추측된다.

투표 메시지(vote_msg)에는 검증자 자신에게 부여된 번호(validator_index), target 체크포인트의 블록 해쉬값(target_hash), target 체크포인트가 포함된 epoch(target_epoch), source 체크포인트가 포함된 epoch(source_epoch), 검증자의 서명(sig)이 담겨있다. 이 정보들을 꺼내어 저장하고(375~380), 해당 검증인이 이미 같은 target_epoch에 투표를 하지는 않았는지 확인한다(384). 하나의 epoch당 검증인이 하나의 투표만을 할 수 있기 때문에 이를 확인한다. 이후에 target_hash가 캐스퍼 컨트랙트가 계산한 체크포인트의 해쉬값과 같은지 확인하고(387), 현재 epoch(current_epoch)을 target으로 하는 투표가 맞는지 확인한다(388). 이 조건을 통해 앞서 언급하였던 투표의 마감기한이 해당 epoch으로 설정되는 것이다. 또한 투표의 source가 justify된 epoch에 포함되어 있는 체크포인트인지 확인한다(390). 이어서 검증인의 자격을 확인하는데, 검증인의 자격이 활성화되는 기간 안에 현재 dynasty(current_dynasty) 혹은 직전 dynasty(past_dynasty)가 포함되는지 확인한다(392~400). 하나의 체크포인트에 대한 투표는 현재 dynasty의 검증인 집합과 직전 dynasty의 검증인 집합이 모두 참여하는데, 두 집합의 ⅔ 이상이 모두 투표해야만 justify된다. 그 이유는 동적 검증인 집합에서 생길 수 있는 문제를 해결하기 위함이다. (1–1. Capser Overview 참고)

투표의 source와 target, 검증인 자격이 모두 확인되면 투표가 정상적인 것으로 판단하고 이를 처리한다. 먼저 검증인이 target_epoch에 대한 투표를 진행했음을 기록한다(402). 또한 하나의 체크포인트는 현재 dynasty 검증인 집단과 직전 dynasty 검증인 집단이 각각 모두 ⅔ 이상 투표해야 justify될 수 있으므로, 이를 각각 기록해야 한다. 따라서 현재 투표 메시지를 게시한 검증인이 현재 dynasty에 포함된 검증인인지, 직전 dynasty에 포함된 검증인인지에 따라, 투표한 검증인들의 예치금 합을 저장한 배열(votes[target_epoch].###_dyn_votes[source_epoch])에 검증인의 예치금만큼을 더한다(406~413). 그 다음, 투표를 제때 제출한 검증인에게는 보상을 한다. 캐스퍼 컨트랙트는 마지막 finalize된 epoch 직후의 epoch을 expected_source_epoch라는 값으로 저장하고 있는데, 이것과 똑같은 source_epoch으로부터의 투표라면 보상을 지급하는 것이다(416~418). 이러한 설계를 통해 프로토콜이 원하는 투표로 유도할 수 있다. 마지막으로, target 체크포인트가 justify 혹은 finalize될 수 있는 상태인지 확인하여 이를 처리한다. 만약 아직 target_epoch이 justify되어 있지 않고, 두 검증인 집단이 각각 ⅔ 이상 투표했다면, target_epoch을 justify한다(421~426). 또한, target_epoch이 justify되었고 source_epoch1만큼 차이 난다면 연속적인 epoch이라는 뜻이므로 finalize시킨다(433~435).

5. slash

마지막 파트는 검증인이 캐스퍼 프로토콜의 규칙을 어겼을 때 이 검증인의 예치금을 몰수하는 slash이다. 우선 이 코드가 call되는 상황을 이해하는 것이 중요하다. 캐스퍼에서 slash는 신고와 포상 제도로 운영되는데, 특정 검증인이 제출한 2개의 투표 메시지가 캐스퍼 프로토콜 규칙을 어긴 것을 발견한 노드가 이를 캐스퍼 컨트랙트에 신고하고, 컨트랙트가 이를 바탕으로 처벌 및 포상하는 방식이다. 따라서 slash를 call할 때에는 이 2개의 투표 메시지(vote_msg_1, vote_msg_2)를 함께 보내야 한다. 두 개의 투표 메시지로부터 정보들을 꺼낸 후에(446~459), 이 두 메시지가 동일한 검증인이 제출한 투표인지(463), 서로 다른 투표 메시지인지 확인한다(465). 그 다음, 프로토콜 규칙 1을 어겼는지 확인하고(468~470), 규칙 2를 어겼는지 확인한다(471~474). 만약 어느 하나의 규칙도 어기지 않았다면 return한다(475). 만약 검증인이 규칙을 어긴 것이 맞다면, 이 검증인의 예치금을 몰수하고 검증인 집단으로부터 삭제하며 이를 발견한 신고자에게 포상금을 제공한다. 이 예치금의 포상금에서 4%만큼을 신고자의 포상금(slashing_bounty)으로 제외하고, 나머지 96%를 destroy하여 total_destroyed에 합한다(477~480). 해당 검증인은 바로 다음 dynasty부터 검증인 집단에서 제외되므로, 직후 dynasty의 deposit 변화량에서 이 예치금만큼을 뺀다(485). 이 때, 만약 이 검증인이 이미 로그아웃을 신청하였다면, logout을 처리하면서 이미 이 검증인의 end_dynasty부터 예치금만큼을 빼는 작업을 수행하였으므로 이를 다시 더해줘야 한다(489~490). 로그아웃을 신청했다는 사실은 검증인의 end_dynasty가 default(무한대)보다 작은 값인지를 확인함으로써 알 수 있다. 모든 과정이 처리되면, 별도의 function(delete_validator())을 call하여 해당 검증인을 검증인 집단으로부터 삭제한다(492). 마지막으로 이를 신고한 사람(msg.sender)에게 포상금(slashing_bounty)을 보낸다(493).

본 포스팅은 암호화폐 거래소 고팍스(@gopaxkr)의 후원을 받아 디사이퍼가 작성하였습니다.

Sort:  

Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://medium.com/decipher-media/scailabilty-series-1-2-casper-code-review-8a84103990d1

Congratulations @decipher-media! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - Quarter Finals - Day 1


Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes


Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Congratulations @decipher-media! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

You got your First payout
Award for the total payout received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - Play-off for third result


Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes


Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Congratulations @decipher-media! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!