안녕하세요, 이더리움 코드보는 Sigmoid입니다.
오늘은 체인의 re-organization에대해서 코드를 살펴보았습니다.
re-organization이란 어떤 체인의 상태가 새로운 블록을 삽입하기에 적합하지 않을 경우, 해당 블록을 삽입할수 있도록 체인을 재구성하는 것입니다. 주로 수신한 블록을 검증하여 삽입할때 내가 소유한 캐노니컬 체인(합의된 체인)이 해당 블록보다 더 길거나, 짧을때 발생합니다.
먼저 우리는 예전에 블록을 체인에 넣을때 아래와 같은 작업들이 일어난다는 것을 분석했었습니다.
(참조: 이더리움 - 블록을 체인에 넣을때 생기는 일 - https://steemit.com/ethereum/@sigmoid/4cknbv)
블록 삽입 시퀀스를 간단히 살펴보자면,
- 수신한 블록의 리스트가 있을때, InsertChain함수를 호출합니다.
- 수신한 블록들의 헤더와 seal을 검증합니다(nonce검증)
2.1 헤더의 엑스트라 데이터가 유효한 크기인지 확인한다
2.2 헤더의 타임스템프를 확인한다
2.3 블록의 난이도가 타임스템프와 부모의 난이도를 기반으로 했는지 확인한다
2.4 Verify that the gas limit is <= 2^63-1
2.5 Verify that the gasUsed is <= gasLimit
2.6 Verify that the gas limit remains within allowed bounds
2.7 블록넘버가 부모의 번호의 +1 인지 확인한다
2.8 합의엔진에 따른 seal이 안전한지 확인한다(주어진 블록이 PoW 난이도 요구사항을 만족하는지 체크한다)
2.9 hard forks (DAO, EIP 150) 블록이라면 제약조건을 만족하는지 확인함 - 블록의 바디를 검증한다
3.1 엉클 검증
3.1.1 블록에대한 엉클의 갯수가 2개 이하인지 확인
3.1.2 최대 7세대까지 거슬러 올라가 엉클로 판별된 블록들간의 공통조상 블록을 검색함
3.1.3 엉클 블록의 해시를 생성하고 조상 블록의 헤더를 검증
3.2 엉클 해시 계산하여 헤더의 엉클블록 해시와 맞는지 비교
3.3 트렌젝션해시생성하고 헤더의 트렌젝션 루트와 맞는지 검증 - 부모블록을 기반으로 스테이트를 생성 (부모블록의 스테이트 루트로 하는 Trie를 Open한다)
- 생성한 부모의 상태를 기반으로 블록을 처리한다
5.1 statedb를 이용하여 트렌젝션 메시지를 실행
5.2 블록 생성자와 포함된 모든 엉클들에게 보상을 적용한다
5.3 트렌젝션 처리과정동안 발생한 영수증과 로그들을 누적하고 그동안 사용된 가스의 량을 반환한다
5.4 만약 어떤 트렌젝션이 가스가 모자라 실행에 실패할 경우 에러를 반환한다 - 스테이트를 검증한다
6.1 가스사용량을 체크한다
6.2 생성된 영수중으로부터 유도된 블록의 블룸과 수신한 블록의 블룸을 검증한다.
6.3 상태 루트를 수신한 루트와 대조하여 검증하고 서로 맞지 않을 경우 에러를 반환한다 - 블록과 상태정보, 영수증을 체인에 쓴다
7.1 Total Difficulty를 계산한다
7.2 블록을 DB에쓴다
7.3 상태정보를 DB에 쓴다
7.4 영수증을 DB에 쓴다
7.5 Total Difficulty가 캐노니컬 체인보다 높을 경우, 캐노니컬 체인에 추가한다
7.5.1 삽입하려는 블록의 부모가, 캐노니컬 체인의 헤드(최신블록)이 아닐경우 <- 이때 reorg가 발생합니다.
예를 들어 내가 8번 블록을 수신했고 검증하여 삽입하려고 합니다.
내가 현재 가지고 있는 캐노니컬 체인의 최신 블록(헤더) 번호가 9라면 re-org를 통해 8번을 넣을수 있는 상태로, 즉 헤드가 7인 상태로 캐노니컬 체인을 만들어내야 합니다.
두가지의 경우가 발생할텐데요, 먼저 내가 가진 캐노니컬 체인이 더 긴경우에는 삭제되어야 할 블록과, 트렌젝션, 영수증 로그들을 검색하여 리스트를 생성합니다. 반대로 더 짧은경우에는 추가되어야 할 블록들의 리스트를 생성합니다.
(기존체인에 블록리스트로 추가할 경우 뭐가 추가되어야 하는지 아는것이 가능하죠?)
이 리스트들을 가지고 추가되거나 삭제된 트렌젝션의 diff를 계산해내고 해당 트렌젝션에 대한 로그와 트렌젝션 해시를 DB로부터 삭제합니다.
첫번째 궁금증: 스테이트도 돌려놔야 하는거 아닌가요?
이미 8번 블록을 검증하면서 스테이트 관련 처리는 끝났기 때문에. 스테이트를 돌려놓을 필요는 없습니다. (제기억으로 스테이트를 돌려놓는 경우는 트렌젝션 실행/검증시 실패가 발생했었을 경우인데요, 관련해서 확인이 추가로 필요하겠네요)
두번째 궁금증: 사라진 트렌젝션은 다시 살려놔야 하는거 아닌가요? (개인의견입니다. 저도 조금 애매해서)
re-org가 발생하는 상황에 대해 조금 생각해보면, 내 로컬 캐노니컬 체인이 길다는 것은 확률적으로
내가 마이닝해서 붙였을 확률이 가장 높습니다. 그런 경우 내 트렌젝션 풀에서는 트렌젝션들이 사라지지만, broadcasting되어 공유된 트렌젝션들은 여전히 남아 있을것이고, 언젠가는 다시 블록으로 들어올것이라는 생각이네요.
생각해 볼수록 재미있는 문제같습니다.
이래서 블록체인이 재미있죠!