Foundry의 스냅샷 싱크 기술 — 베리싱크(VeriSync)

Junha Yang
코드체인
Published in
6 min readJan 23, 2020
Photo by Luciano Zanollo on Unsplash

블록체인에 참여하고자 하는 노드들은 체인의 정보를 받아오고 이를 유지해야 합니다. 라이트 클라이언트는 체인에 쓰여진 트랜잭션이나 스테이트를 검증하는 것이 목표이기 때문에 블록의 헤더들만 검증하고 관리하는 것으로 충분합니다. 반면 블록의 생성에 관여하는 풀 노드들은 블록 전체를 검증해야 하고 그를 위해 트랜잭션, 스테이트를 전부 들고 있어야 합니다. 만약 누군가가 새로 풀 노드로 참여하고자 한다면 최신 블록까지 동기화를 해야 하고, 따라서 제네시스 블록으로부터 헤더를 검증하고 트랜잭션을 수행하여 스테이트를 변화시키는 시뮬레이션을 모든 블록에 대해 수행하여야 합니다. 이는 상당히 시간이 오래 걸리는 작업이며 체인이 길어지면 더더욱 심해질 것입니다.

그러나 어느정도 최신 시점에 신뢰할 수 있는 헤더를 이미 가지고 있다면 새로운 접근을 시도할 수 있습니다. 신뢰할 수 있는 헤더에 해당하는 블록의 스테이트를 신뢰할 수 없는 풀 노드로부터 받아와도 이를 머클 루트를 통해 검증할 수 있기 때문입니다. 이런식으로 검증된 헤더만 가지고 다른 풀 노드로 부터 스테이트를 받아와 이를 검증한 후 풀 노드로 태어나는 방법은 처음부터 풀 싱크를 하는 것에 비해 매우 효율적이며 빠를 뿐만 아니라 신뢰할 수 있는 방법입니다. 다른 풀 노드가 이런 요청에 대비해 특정 블록의 스테이트의 기록을 남기는 것을 ‘스냅샷’이라고 하며 그 스냅샷을 이용하여 풀 노드로 빠르게 동기화 하는 Foundry의 기술을 베리싱크(VeriSync)라고 부릅니다.

원리

원리는 간단합니다. 신뢰할 수 있는 헤더를 갖고 있으면 그 헤더에 해당하는 블록의 모든 스테이트를 다른 풀 노드로부터 받아온 다음 머클 루트를 비교해 검증하고, 그 시점부터 모든 정보를 가지고 있는 새로운 풀 노드가 될 수 있습니다. 신뢰할 수 있는 헤더 하나를 얻는 방법은 자유롭습니다. 첫 번째 방법은 라이트 클라이언트처럼 제네시스부터 해당 헤더까지 헤더싱크를 거쳐 혼자서 검증된 헤더를 얻어내는 방법입니다. 그 누구도 신뢰할 필요가 없지만 Long range attack을 막을 수 없고, 시간이 오래걸리며 풀 노드들이 과거의 모든 위원회 집합을 알고 있어야하는 제약이 있습니다. 두 번째 방법은 신뢰할 수 있는 헤더가 있다면 그냥 그 헤더를 쓰는 것입니다. 컨센선스를 넘어서는 사회적인 방법으로 헤더를 찾아야하지만, 쉽고 빠르며 Long range attack를 막을 수 있습니다. Foundry의 경우 일단 후자의 방법을 선택했으며 이 경우에 단 하나의 헤더만 준비하면 됩니다. 그렇게 받아온 헤더에 대해 스테이트를 동기화한 이후에는 다른 풀 노드와 같은 방식으로 트랜잭션 직접 수행을 통해 최신 블록까지 가게 됩니다. 그러면 마침내 블록을 실제로 생성하고 검증할 수 있는 풀 노드가 되며, 이후에도 트랜잭션을 수행하고 스테이트를 업데이트하며 자신의 DB를 업데이트해 나갑니다.

구현

동작 방식은 간단하지만 고려할 사항이 몇 개 있습니다. 단 한 블록의 스테이트라고 해도 그 양이 큰데, 그 큰 스테이트를 전부 다운로드 받은 후에야 검증을 할 수 있다는 것입니다. 베리싱크로 시작하려는 노드들은 신뢰하는 헤더만 갖고 있으면 거짓 스테이트를 받아도 검증할 수 있지만 이를 하기 위해 일단 스테이트 전체를 다운받아야 하기 때문에 네트워크 소모가 큽니다. 여러 비잔틴 노드들에게 속는 경우, 혹은 네트워크 상태가 안 좋아 다운로드가 끊기는 경우에는 스테이트 전체를 다운받았다가 검증에 실패해 지우고 다른 노드를 찾는 작업을 반복하게 될 것입니다.

이를 해결하기 위해 Foundry에서는 스테이트 머클트리를 청크로 나누는 테크닉을 이용했습니다. 청크는 트리의 일부입니다. 서브트리와 다르게 원래 트리의 최하단까지 포함할 필요가 없기 때문에 트리의 중간 부분만 떼어서 청크라고 부를 수 있습니다. 즉 서브트리는 청크지만 청크는 서브트리가 아닙니다.

머클트리는 그 루트가 헤더에 쓰여서 합의되어야 할 값이기 때문에 결정론적으로 구조가 형성됩니다. 즉 말단인 잎(Leaf) 노드들만 가지고 가지(Branch) 노드를 만들 수 있으며 최종적으로는 트리 전체를 재구성할 수 있는 특성이 있습니다. 따라서 각 청크에서도 잎 노드들만 다운받으면 해당 청크 전체를 복원할 수 있습니다.

이런 각각의 청크들은 어떤 순서로 다운로드해야할까요? 제일 처음 헤더에 적혀있는 최종 머클 루트에 바로 이어지는 최상단 청크를 하나 받아온다면 이는 즉시 검증 가능합니다. 청크의 루트와 바로 비교하면 되기 때문입니다. 그 다음에는 비슷한 방식으로 최상단 청크의 자식 청크들을 받아와서 검증된 최상단 청크의 말단 가지 노드들과 비교하여 검증을 진행할 수 있고, 이를 반복하면 최종적으로 트리 전체를 완성하기까지의 각각의 청크들을 매번 점진적으로 검증해나갈 수 있습니다.

이런 식으로 위에서부터 청크를 하나하나 받아오는 방식을 이용하면 만약 스테이트가 잘못된 정보라고 해도 베리싱크를 진행하는 노드는 스테이트를 전부 다운받지 않고 중간에 끝내버릴 수 있습니다. 또한 스테이트를 작은 청크로 나누는 것은 추가적인 분산 최적화에 이용될 여지가 있습니다. 여러 노드들에게 골고루 요청하여 네트워킹 처리량을 높일 수 있기 때문입니다.

이렇게 스냅샷 싱크로 시작한 풀 노드는 이전 블록에 대한 스테이트는 전혀 갖고 있지 않다는 특징이 있습니다. 이는 이전의 여러 블록을 봐야 하는 특정 스테이킹 룰을 검증할 수 없다는 점에서 문제가 됩니다. 코드체인은 위훤회가 정해지는 ‘텀’의 시작에서만 스냅샷을 저장하게 함으로써 이 문제를 해결했습니다. 다만 이것은 지난 텀의 블록까지 볼 필요는 없는 스테이킹 룰의 지엽적인 특성을 이용한 것이고, 그것과 별개로 전 블록들을 검증하고 있는 노드들의 쿼리에 답해줄 수 없다는 문제가 있기 때문에 좀 더 고민해볼 필요가 있습니다.

결과

코드체인은 스냅샷 싱크를 구현했으며 그 결과를 분석했습니다. 테스트 네트워크의 3,197,000번째 블록에 대해서 풀 싱크와 스냅샷 싱크를 각각 수행하여 소요된 연산을 비교해봤는데, 그 결과는 주목할만했습니다. 소요된 총 시간은 3분 46초로, 풀 싱크시 소요되는 10시간 40분에 비하면 약 169배 빠릅니다. 또한 1달 치 스냅샷의 용량은 1.6GB로 전체 데이터베이스의 약 5%입니다. 벤치마크 테스트를 하기 위해서 특정 블록까지의 풀 싱크의 결과와 비교를 했는데, 사실 풀 싱크에 소요되는 자원은 체인이 길어질수록 더 커집니다. 따라서 이 실험에서 보여주는 성능 향상은 시간이 가면 갈수록 더 극적으로 바뀔 것이고 베리싱크의 필요성은 더욱 커질 것입니다.

마치며

이상으로 코드체인의 신기술인 베리싱크에 대해 알아보았습니다. 체인이 커질수록 풀 노드로 참여하기 위한 동기화 작업량이 커지고, 이는 체인 전체의 접근성에 걸림돌이 됩니다. 대신에 검증할 수 있는 방법으로 주변 풀 노드들에게서 스테이트를 받아오는 베리싱크는 빠르고 효율적이며 분산적으로 동작합니다. 우리 Foundry는 이런 코어 기술들을 집약적으로 적용시켜 완성도 높은 블록체인 코어를 제공하는 것을 목표로 하며, 여러 블록체인 어플리케이션 개발자들이 Foundry를 이용하여 쉽게 퀄리티 높은 체인을 만들 수 있게 합니다.

소스코드

--

--