블록체인 확장성 솔루션 시리즈 5–1 :: Truebit A to Z

Geon-gi Mun (Geon-gi Mun)
Yeongtaek OH (Robbie OH)
Seoul Nat’l Univ. Blockchain Academy Decipher (Medium)

서울대학교 블록체인 학회 ‘디사이퍼(Decipher)’에서 블록체인의 확장성 솔루션에 관한 글들을 시리즈로 연재합니다. 시리즈의 다섯 번째 주인공은 “Truebit”입니다. 5–1. Truebit A to Z, 5–2. Truebit Code Review 로 나누어 설명합니다.

다섯 번째 주제로 이더리움의 연산 성능을 올리기 위한 오프체인(off-chain) 솔루션인 트루빗에 대해 알아보고자 합니다. 이전까지의 시리즈는 이더리움 재단 주도의 대중적인 확장성 솔루션들을 다루어 왔습니다. 하지만 최근에는 이더리움 진영 외에도 확장성을 해결하고자 하는 아주 다양한 시도들이 이루어지고 있습니다. 다양한 문제 의식과 해결 아이디어들을 공유하고자 조금은 생소한 프로젝트인 트루빗을 소개합니다.

트루빗(Truebit)은 카이버 네트워크의 로이 루(Loi Luu)와 함께 Verifier’s dilemma를 쓴 Jason Teutsch가 만들고 있는 솔루션입니다. 트루빗의 백서Medium 글, Github을 바탕으로 트루빗이 해결하고자 하는 문제는 무엇이며, 이를 어떻게 해결하고자 하는지 알아보도록 하겠습니다.

글은 크게 다음의 흐름으로 진행됩니다.

1. 트루빗의 등장 배경
2. 트루빗의 구동 원리
3. 트루빗의 경제 유인 설계
4. 트루빗의 한계점 / 의문점
5. 정리

트루빗의 등장 배경 — decentralized computation

최근 다양하게 제시되고 있는 확장성 솔루션들은 대체로 메인 체인의 트랜잭션 처리 성능을 개선하여 TPS(Transaction Per Second)를 높이는 데에 중점을 둔 경우가 많습니다. 샤딩(Sharding)이나 플라즈마(Plasma) 등의 많은 솔루션이 이에 해당한다고 할 수 있습니다.

이에 반해 트루빗은 이더리움의 TPS가 아닌, 이더리움에서 처리할 수 있는 연산 성능의 한계를 극복하기 위해 등장한 확장성 솔루션입니다. 이더리움에서 프로그램을 실행하기 위해서는 프로그램의 연산 복잡도에 비례하게 가스를 지불해야 하는데, 한 블록 안에 담길 수 있는 가스의 총량이 제한되어 있기 때문에 아주 복잡한 프로그램은 구동할 수 없습니다. 이에 대해 조금 더 자세히 알아보도록 하겠습니다.

이더리움에서 Solidity로 코드를 작성하여 배포하면 EVM (Ethereum Virtual Machine) 바이트 코드로 컴파일된 상태로 블록체인에 저장됩니다. 그 이후로 해당 코드가 저장된 주소로 트랜잭션이 전송될 때마다 코드가 실행됩니다. 모든 이더리움 채굴자가 해당 코드의 연산을 실행하고, 이를 통해 블록체인 전체가 하나의 코드 실행 결과로 합의하게 됩니다. 이 때 코드의 실행에 쓰이는 연산량을 계산하여 트랜잭션 송신자가 수수료로 지불하도록 되어 있습니다. 이더리움에서는 수수료를 ‘가스’라고 부르며 블록체인 가상 머신 명령어 세트의 각 연산별로 수수료를 할당하고 있습니다. 따라서 코드의 복잡도에 비례하여 수수료가 증가하게 되는 구조이며, 이는 단순한 비즈니스 로직을 넘어선 수준의 코드를 실행하기 위해서는 엄청난 수수료를 지불해야 함을 의미합니다.

그렇다면 막대한 수수료를 지불할 생각이 있는 사람은 이더리움에서 고성능 프로그램을 구동시킬 수 있을까요? 그렇지 않습니다. 이더리움에는 블록 가스 제한(block gas limit)이라는 것이 존재하기 때문입니다. 블록 가스 제한이란 한 블록 내의 모든 거래에서 소비될 수 있는 총 가스의 상한선을 의미합니다. 이 블록 가스 제한보다 더 많은 가스를 소모하는 코드를 실행시키는 트랜잭션은 블록 안에 담길 수 없게 되는 것이죠.

이더리움 블록 가스 제한 평균치: 현재 800만 가스 (출처: etherchain.org)

결국 연산 성능의 한계가 블록 가스 제한으로부터 온다면, 이를 높여서 더 많은 연산이 수행될 수 있도록 할 수 있을 것입니다. 이를 통해 조금은 완화할 수 있고 실제로 계속해서 높여오고 있지만, 불행하게도 ‘Verifier의 딜레마’ 때문에 블록 가스 제한을 무한정 높일 수 없습니다. ‘Verifier의 딜레마’란 이더리움의 채굴자들이 겪는 딜레마를 말합니다. 이더리움 채굴자는 새롭게 형성된 블록을 전파받았을 때 다음 블록 채굴을 시작하기 전에 전파받은 블록이 정확한 블록인지 먼저 검증할 필요가 있습니다. 만약 새롭게 전파받은 블록에 코드를 실행하는 트랜잭션이 담겨 있다면 이 연산 역시 진행해서 스마트 컨트랙트의 모든 상태가 올바른지 확인해야 합니다. 하지만 이 검증에 대해서는 채굴자들이 보상(수수료)을 받지 못합니다. 게다가 이 검증에 시간이 오래 걸린다면 채굴자들은 이 작업을 건너뛰고 바로 채굴을 시작하여 다른 채굴자보다 빠르게 채굴하고 싶어질 것입니다. 하지만 그렇다고 해서 검증을 하지 않고 그 위에 블록을 붙였다가는 잘못된 체인에 시간만 낭비하는 셈이 될 수 있습니다. 따라서 합리적인 채굴자들은 이 검증을 할지 또는 건너뛸지 두 가지 상반된 행위 사이에서 고민하게 됩니다. 이 때 이러한 고민은 블록 가스 제한이 늘어날수록 심해집니다. 검증에 걸리는 시간과 비용이 증가할수록 건너뛰고자 하는 유인이 늘어날 것이기 때문이죠.

Verifier의 딜레마 정리

그래서 이러한 온체인 연산 성능의 한계를 검증 가능한 오프체인 솔루션으로 해결해보려고 등장한 것이 바로 트루빗입니다.

트루빗의 구동 원리 (Off-chain Computation Solution)

그러면 이제 트루빗이 어떻게 이 문제를 해결하고자 하는지 알아보도록 하겠습니다. 요약하여 말하자면, 어떤 dapp (decentralized application)이 소모되는 가스가 블록 가스 제한보다 높은 연산을 수행하고 싶을 때 트루빗 프로토콜로 이 연산을 넘겨주면, 트루빗 네트워크 참여자들이 오프체인에서 이를 대신 수행하고 결과값을 전달하여 dapp이 다시 결과값만을 온체인에 반영하는 형태입니다. 좀 더 상세히 구동 원리를 알아보도록 하겠습니다.

트루빗 컨트랙트 (Truebit Contracts)

먼저 이더리움 온체인에 트루빗 스마트 컨트랙트가 있습니다. 이 스마트 컨트랙트 안에는 createTask라는 function이 있으며, 이 function을 통해 dapp과 트루빗 프로토콜이 통신할 수 있습니다. dapp이 createTask를 호출할 때에는 다음과 같은 3가지 매개변수를 포함해야 합니다.

  • program: 구동하고자 하는 프로그램 코드. 트루빗은 WebAssembly VM를 사용합니다. 컴파일된 바이트코드 형태로 넘길 수도 있고, 혹은 IPFS의 해시값이나 코드가 저장된 다른 위치의 주소를 넘길 수도 있습니다.
  • inputs: 실행시킬 프로그램의 인풋 값. dapp은 이를 직접 넘길 수도 있고, 마찬가지로 인풋 값이 저장된 다른 위치의 주소를 넘길 수도 있습니다.
  • reward: 해당 프로그램을 실행시키는 사람에게 전달할 보상.
Dapp이 트루빗 task를 생성합니다. (출처: 트루빗 Medium)

좀 더 쉬운 이해를 위해 예를 들어 설명해보도록 하겠습니다. 탈중앙화 조직 운영 플랫폼인 아라곤(Aragon)에서 투표 결과를 수합하여 계산하고자 합니다. 이 때 투표 기록을 담은 배열이 매우 크다면, 온체인에서 투표 결과를 수합하는 연산을 처리하는 것은 비쌀 뿐 아니라 블록 가스 제한을 초과할 수 있습니다. 그래서 아라곤은 투표 기록을 수합하는 프로그램 코드를 program으로, 투표 기록을 담은 배열을 input으로 하여 트루빗 컨트랙트의 createTask function을 호출합니다. 이로써 트루빗 컨트랙트에 새로운 task가 생성됩니다.

트루빗 네트워크 (Truebit Network)

다음으로 트루빗 컨트랙트에 생성된 task를 오프체인의 트루빗 네트워크에서 어떻게 처리하는지 알아보도록 하겠습니다. 트루빗 클라이언트를 설치한 사람은 누구나 트루빗 네트워크의 채굴자가 될 수 있으며, 이들은 트루빗 컨트랙트의 이벤트를 listen하고 있습니다.

task를 listen하고 있는 트루빗 채굴자들 (출처: 트루빗 Medium)

트루빗 채굴자들은 트루빗 컨트랙트에 새로운 task가 생기면 이 프로그램 코드를 다운로드하고, 그들의 로컬 Truebit WebAssembly VM에서 주어진 인풋과 함께 실행하여 연산의 결과값을 컨트랙트에 제출합니다. 이 때 연산을 수행하여 문제를 푼 채굴자를 문제 해결자(Solver)라고 하겠습니다. 문제 해결자는 결과값을 제출할 때 일정량의 보증금을 함께 제출해야 합니다. 이 보증금을 통해 자신의 결과값이 옳은 결과값이라는 것을 담보하는 셈이죠.

문제 해결자가 답안을 제출합니다. (출처: 트루빗 Medium)

문제 해결자가 결과값을 제출한 시기부터 타이머가 시작됩니다. 이 타이머(challengeTimeout)동안 제출된 결과값을 검증하여 혹시 틀린 결과값이라면 이에 대해 누구나 챌린지를 신청할 수 있습니다. 이 때 챌린지를 신청한 검증자 역시 보증금을 함께 제출해야 합니다.

Case #1. 챌린지가 없는 경우

먼저 타이머동안 아무도 챌린지를 제출하지 않았다면 트루빗 네트워크는 문제 해결자가 올바르게 코드 연산을 수행했다고 생각할 것입니다. 따라서 이 때에는 트루빗 컨트랙트가 dapp 컨트랙트로 해당 결과값을 다시 콜백하여 줍니다.

Dapp이 답안과 함께 callback을 받습니다. (출처: 트루빗 Medium)

Case #2. 챌린지가 제출된 경우

반면에 타이머동안 검증자가 보증금과 함께 챌린지를 제출한 상황을 살펴보도록 하겠습니다. 잠깐 정리해보면, 이제 트루빗 컨트랙트에는 task 생성자(Task Giver)가 제시한 보상, 문제 해결자의 보증금, 검증자의 보증금 총 3가지 금액이 저장되어 있겠네요.

검증자가 챌린지를 제출합니다. (출처: 트루빗 Medium)

이제부터 문제 해결자와 챌린지를 제출한 검증자 사이에서 누구의 연산 결과값이 옳은지에 대한 ‘검증 게임(verification game)’이 시작됩니다.

검증 게임 (The verification game)

검증 게임을 이해하기에 앞서, 트루빗에서 처리하는 모든 task의 프로그램 코드는 웹어셈블리 프로그램이며, 웹어셈블리는 매 명령어들이 하나씩 순서대로 실행된다는 것을 기억하고 있어야 합니다. (물론 EVM의 JUMP, WebAssembly의 br과 같은 종류의 명령어가 있다면 실제 컴파일된 순서와 실행 순서가 다를 수 있습니다. 이 부분에 대해서는 글의 후반부인 한계점 / 의문점에서 짚어보도록 하겠습니다.) 결국 몇 번째 명령어를 실행할 때 서로의 연산 결과가 달라지기 시작했는지를 알아내고, 이를 온체인에서 실행하여 누가 맞았는지를 확인하는 것이 게임의 요지이기 때문입니다.

간단한 C 프로그램(왼쪽)과 이를 WebAssembly로 컴파일한 결과(오른쪽) (출처: 트루빗 Medium)

또한, 검증 게임에는 3가지 종류의 참여자가 존재함을 기억해야 합니다.

  • 문제 해결자(Solver): task에 대한 연산을 수행하고 결과값을 제출한 사람
  • 챌린저(Challenger): 문제 해결자의 결과값이 틀렸음을 주장하는 사람
  • 판사(Judges): 제한된 연산 능력을 지녔으며, 결과값이 맞았는지 아닌지를 판단해줄 주체

현재 트루빗은 판사(Judges)를 이더리움 네트워크로 설정하고 있습니다. (실제로 이더리움 네트워크가 망가진다면, 언제든지 이를 다른 네트워크로 옮길 수 있다고 말하고 있습니다.) 따라서 최종적으로 이더리움 네트워크 온체인에서 판단이 내려져야 하며, 이 때 이더리움 네트워크는 연산 능력이 제한되어 있으므로 이 연산 부담을 최소화해야 합니다.

이제 검증 게임을 진행 순서대로 따라가보도록 하겠습니다.

먼저 챌린저가 게임을 시작합니다.

0번째 상태에서는 문제 해결자와 챌린저 모두 같은 프로그램 코드와 인풋을 다운로드하였으며 가상머신도 초기화되어 있을 것이므로 서로 상태가 일치할 것입니다. 하지만 프로그램이 총 14개의 명령어로 이루어져있다고 가정했을 때, 프로그램의 마지막인 14번째 상태에서는 서로 불일치할 것입니다.

챌린저는 이를 활용하여 불일치하기 시작한 지점을 탐색해나갑니다. 가장 간편하고 빠르게 이를 알아낼 수 있는 이진 탐색(binary search)을 활용했다고 가정해봅시다. 챌린저는 문제 해결자에게 프로그램의 절반에 해당하는 단계의 명령어 실행 결과를 질의(query)합니다. 여기서는 7번째 상태가 될 것입니다.

질의를 요청받은 문제 해결자는 특정 시간 안에 이 질의에 응답해야 합니다. 만약 제시간 안에 응답하지 않으면 즉각적으로 보증금을 잃게 됩니다. 문제 해결자는 트루빗 웹어셈블리 VM을 이용하여 7번째 상태의 해시를 계산하여 제출함으로써 이 질의에 응답(respond)합니다. 이 때 7번째 상태의 해시란, 7번째 명령어를 실행한 직후의 스택, 프로그램 카운터 등 VM의 전체 상태의 머클 루트를 의미합니다.

문제 해결자와 챌린저가 검증 게임을 진행합니다. (출처: 트루빗 Medium)

문제 해결자의 응답을 확인한 챌린저는 로컬 트루빗 웹어셈블리 VM을 활용하여 자신의 머클 루트를 계산하고, 이를 문제 해결자의 값과 비교합니다.

만약 두 값이 같다면, 7번째까지의 명령어 수행 결과는 일치한다는 뜻이므로 그 이후의 절반 상태(8~14번째)에서 불일치가 생겼을 것입니다. 반면에 두 값이 다르다면, 앞 쪽의 절반 상태(1~6번째)에서 불일치가 생겼을 것입니다.

여기서는 두 값이 같았다고 가정해봅시다.

그러면 챌린저는 다시 후반부의 절반에 해당하는 10번째 상태를 문제 해결자에게 질의합니다.

문제 해결자가 응답할 것입니다. 만약 이번에도 같았다면, 또 다시 후반부의 절반인 12번째 상태를 질의합니다.

이런 식으로 상호간의 게임이 진행됩니다.

결국 챌린저는 문제 해결자와의 불일치가 시작되는 지점을 찾아내기 위하여 이진 탐색(binary search)하므로, 전체 프로그램이 n개의 명령어로 이루어져 있다면 총 O(log(n))만큼의 단계가 필요합니다.

드디어 문제 해결자와 챌린저 사이의 불일치가 시작되는 지점인 상태(13번째 명령어 실행 직후의 상태)를 찾아냈다고 합시다.

이제 과연 누가 옳은지를 확인하기 위해 온체인에서 13번째 명령어에 해당하는 연산을 검증하는 과정을 거칩니다. 하나의 연산만 수행하면 되기 때문에 적은 수수료만으로 온체인에서 구동해볼 만한 수준의 연산이 된 것입니다.

온체인 웹어셈블리 인터프리터가 불일치가 시작되는 명령어를 실행합니다. (출처: 트루빗 Medium)

그러면 온체인에서 해당 연산을 어떻게 수행하고 검증할까요? 트루빗 스마트 컨트랙트에는 웹어셈블리 인터프리터(interpreter)가 내장되어 있습니다. 이 가상머신을 13번째 명령어를 시작하기 직전의 상태로 초기화한 후에, 13번째 명령어에 해당하는 연산을 수행합니다. 그 다음 스택, 메모리, 가상머신 상태에 대해 똑같이 머클 루트를 계산하고 이를 문제 해결자의 머클 루트와 비교합니다. 만약 그 값이 서로 다르다면, 문제 해결자의 연산이 틀렸다는 뜻이므로 문제 해결자의 보증금을 삭감함으로써 검증 게임을 마무리합니다. 만약 문제 해결자의 연산이 옳았다면, 지금까지 검증 게임을 하면서 생긴 비용을 챌린저의 보증금에서 삭감합니다.

트루빗은 전체 연산을 오프체인으로 수행하고, 연산 결과에 대해 논쟁(dispute)이 발생했을 때 단일 지점에 대한 연산만을 이더리움 온체인에서 수행하도록 설계하였습니다. 트루빗 재단은 이러한 트루빗 프로토콜의 합의 메커니즘이 과반수가 정직해야 하는 나카모토 컨센서스나 ⅔ 이상이 정직해야 하는 BFT보다 강력하다고 주장합니다. 그 이유는 1명의 정직한 검증자만 존재한다면 적어도 틀린 연산 결과가 제출될 일은 없기 때문이라는 것이죠. 하지만 이러한 설계 의도대로 네트워크가 움직이기 위해서는 여러 경제적인 유인에 대한 고민이 필요합니다. 검증자가 항상 문제 해결자의 답안 제출을 검증해야 할 유인, 위험을 감수하고 검증자가 아닌 문제 해결자로 활동할 유인, 검증자와 문제 해결자 사이의 역할 분배 등의 설계가 필요합니다.

트루빗의 경제 유인 설계

그렇다면 트루빗은 의도한 대로 시스템이 구동될 수 있도록 어떠한 인센티브 구조를 설계하였는지 살펴보도록 하겠습니다.

트루빗 네트워크의 불균형 순환 시나리오

먼저 검증자들이 항상 문제 해결자들이 제출한 답안을 검증할 유인이 필요합니다. 검증자들이 잘못된 답안을 찾아낼 때에만 보상을 받는다고 가정하면, 네트워크의 균형을 유지하기 힘듭니다. 문제 해결자가 틀린 답을 내면 검증자들의 기대 수익이 높아지기 때문에 검증자들이 유입될 것입니다. 검증자들이 많아질수록 문제 해결자들은 잘못된 답안을 제출하지 않으려 할 것이며, 이는 다시 검증자들의 기대 수익을 낮춰 검증자들을 이탈시킬 것입니다. 만약 검증자들이 이탈한다면, 다시 문제 해결자들은 잘못된 답안을 제출할 수 있습니다. 짧게 말하면, 네트워크가 균형을 유지하지 않을 가능성이 높다는 것입니다. 이를 해결하기 위해 트루빗은 ‘강제 오류와 잭팟’이라는 개념을 도입하였습니다.

강제 오류와 잭팟 (forced error and jackpot)

‘강제 오류와 잭팟’이란 검증자들이 매번 문제 해결자의 답안을 검증할 유인을 제공하기 위해 설계된 보상체계입니다. 간단히 설명하면 트루빗 컨트랙트가 임의로 문제 해결자가 잘못된 답을 제출하도록 강제하고 이 오류를 찾아낸 검증자에게는 모두 잭팟을 쥐어주는 방식입니다. 검증자들은 언제 강제 오류가 발생할지 모르기 때문에 잭팟을 얻기 위해 매 답안을 검증해야 하는 유인이 생깁니다. 물론 잭팟의 상금은 매 답안을 검증하는 비용을 모두 충당할 만큼의 금액이 되어야 합니다. 그런데 이 때 여러 공격 시나리오를 막기 위해서는 다음과 같은 조건들이 만족되어야 합니다.

Task Giver가 task를 생성할 때까지 이것이 잭팟인지 몰라야 합니다.
Solver가 답안을 제출할 때까지 이것이 잭팟인지 몰라야 합니다.
Verifier가 챌린지를 제출할 때까지 이것이 잭팟인지 몰라야 합니다.

이 3가지 조건을 만족하기 위하여 트루빗은 RANDAO의 메커니즘을 차용하여 다음과 같이 구현하였습니다.

1. 문제 해결자가 답안을 제출할 때마다 옳은 답틀린 답을 함께 제출합니다. 두 답 중에 어느 것이 옳고 틀린지는 밝히지 않습니다. 이와 더불어 특정 문자열 x의 해시값을 함께 제출합니다. 이 때, x는 제출하지 않습니다.

2. 검증자는 두 가지 답을 모두 검증한 뒤에, 틀린 답에 대하여 챌린지를 신청합니다.

3. 챌린지 신청이 끝나면, 문제 해결자는 두 가지 답 중 어느 것이 옳은 답이었는지를 밝힙니다. 이 때, 1번에 쓰였던 문자열 x값도 함께 제출합니다.

4. 블록 넘버와 x값을 더하여 해시값을 만들고 이것이 일정값 이하라면 강제 오류로 판단하여 챌린지를 신청한 모든 검증자에게 잭팟을 나눠주고 task를 마무리합니다.

5. 만약 강제 오류가 아니라면, 문제 해결자가 옳은 답이라고 밝힌 답안에 대해 챌린지를 신청한 검증자들과 검증 게임을 진행합니다.

이렇게 구현하게 되면, 3~4번 트랜잭션이 실리는 블록 넘버를 누구도 예상할 수 없으므로 위에서 말했던 3가지 조건을 만족시킬 수 있습니다. 보다 자세한 내용은 다음 글인 Truebit Code Review에서 실제 코드와 함께 다뤄보도록 하겠습니다.

잭팟 금고와 세금 (jackpot repository and taxes)

이러한 보상 체계를 마련하기 위해서는 잭팟을 통해 지급되는 수익이 일정 수준을 잘 유지하도록 설계되어야 합니다. 왜냐하면 잭팟을 통한 기대 수익(jackpot payout)이 이번 검증 연산에 대한 적합한 보상(task compensation)과 잭팟이 터질 확률(forced error rate, 1/1000으로 고정)을 곱한 것보다 크거나 같아야 최소한 매 task당 1명 이상의 검증자가 존재할 것이라고 보장할 수 있기 때문입니다.

jackpot payout >= task compensation * forced error rate

더불어 이렇게 보상 체계가 마련된다면, 문제 해결자는 고정적인 수입을 얻을 수 있는 데에 비해 검증자는 간헐적인 수입을 얻게 됩니다. 일반적으로 사람들은 고정적인 수입에 비해 간헐적인 수입을 선호하지 않으므로 우변의 곱셈값보다 조금 더 높은 기대 수익이 보장되어야 한다고 말하고 있습니다.

그렇다고 해서 잭팟의 기대 수익이 한없이 높아지는 것도 막아야 합니다. 만약 잭팟의 기대 수익이 매우 높다면, 문제 해결자가 이더리움 채굴자와 공모하여 블록 보상을 포기하면서 잭팟이 터질 때까지 블록 채굴을 지속하는 등의 공격이 가능해지기 때문입니다.

따라서 잭팟 금고가 일정 수준의 금액을 잘 유지하고 있어야 하는 것이 트루빗 경제 설계의 핵심이라고 할 수 있습니다.

잭팟의 금고는 어떻게 유지될까요? 초기에는 트루빗 재단의 기부를 통하여 기금을 조성하는 것으로 설계하고 있습니다. 이후에는 Task Giver들에게 세금(tax)을 부과하여 잭팟 금고의 재원을 충당하게 됩니다. 앞서 구동 원리를 설명하면서 Task Giver는 문제 해결자에 대한 보상을 지급한다고 하였습니다. 사실 이뿐만 아니라 Task Giver는 정당한 챌린지에 대해 검증 게임의 수수료(가스)도 부담해야 합니다. (만약 정당하지 않은 챌린지라면 모든 수수료는 챌린저의 보증금에서 삭감됩니다.) 나아가 이에 더하여 프로토콜에 대한 세금까지 추가적으로 지불하도록 하였습니다. 세율(tax rate)에 대해서는 500%~5000% 정도로 언급하고 있으나 실제 네트워크를 구동해보면서 실험적으로 수치를 정할 필요가 있다고 말하고 있습니다. 마지막으로 잭팟을 통해 1회에 지급되는 금액은 최대 당시 금고 전체 금액의 1/3을 넘을 수 없도록 설계하고 있습니다.

트루빗의 의문점 / 한계점

지금까지 트루빗이 가진 문제 의식과 그들이 제시하는 해결 방안에 대해 살펴보았습니다. 살펴보고 나니 머릿속에 여러 의문점과 한계점들이 떠오릅니다.

1. 과연 프로그램 코드 혹은 컴파일된 바이트코드를 전달받은 트루빗 네트워크 참여자가 이를 실행하기 전까지 연산의 복잡도를 예상할 수 있는가?

Task Giver가 트루빗 컨트랙트에 task를 생성할 때 프로그램 코드와 함께 답안 제출 기한(timeOut) 및 보상(reward)을 함께 제시합니다. 이 때 동적으로 프로그램을 실행해보기 전까지 프로그램이 언제 종료되는지에 대해 알 수 없다는 사실은 이미 잘 알려져 있습니다. 따라서 트루빗 네트워크 참여자가 이 코드를 수행할 때 소요되는 시간과 비용에 대해 미리 예측하기 힘들 것이며, 이를 활용한 공격도 가능할 것이라 예상됩니다.

트루빗의 Github wiki를 참고하면 이를 해결하기 위하여 트루빗은 이더리움의 eWASM과 같이 WASM(WebAssembly) 모듈에 gas를 도입하려고 하고 있습니다. Task Giver가 Gas price와 Gas limit을 함께 제시해야 하며 이를 넘어서는 연산은 종료되도록 하고 있습니다.

2. 컴파일된 상태의 모든 명령어를 연속된 step으로 정의하여 검증 게임을 진행할 수 있는가?

예를 들어 프로그램에 조건문이 있거나 반복문이 있는 경우 컴파일된 명령어의 순서와 실제 실행되는 명령어의 순서가 다를 수 있습니다. 따라서 이를 연속된 일련의 step으로 정의하기 위해서는 컴파일 과정에서 loop unrolling과 같은 작업이 필요합니다. 트루빗의 Github wiki에서 이러한 pre-processing 작업을 개발해야 함을 future works로 제시하고 있습니다.

3. 검증 게임에 소요되는 비용이 너무 크지 않은가?

검증 게임을 진행할 때 매 step마다 query()와 respond()를 반복하는데, 이것이 매번 트루빗 스마트 컨트랙트를 향한 트랜잭션이기 때문에 수수료(가스)가 지불됩니다. 하나의 검증 게임 안에서는 O(log(n))만큼의 복잡도로 특정 명령어를 찾아간다고 하더라도, 여러 챌린지가 제출되었다면 모든 챌린지에 대하여 개별적으로 검증 게임이 진행되어야 합니다. 결과적으로 n개의 명령어로 이루어진 프로그램 코드의 task에 m개의 챌린지가 제출되었다면 O(m⠂log(n)) 만큼의 트랜잭션을 이더리움으로 처리해야 합니다. 이러한 트랜잭션을 처리하는 데에 드는 수수료가 트루빗의 사용성을 저해하는 장애물이 될 수 있습니다.

4. 온체인 WASM 인터프리터의 한계

온체인 웹어셈블리 인터프리터가 처리할 수 있는 성능의 한계가 존재합니다. 단일 명령어만을 온체인에서 수행한다고 하더라도 그 명령어가 내포하고 있는 low-level에서의 작업들은 여러 가지가 될 수 있습니다. 더불어 해당 명령어를 실행하기 직전의 상태로 VM을 초기화하는 작업까지 포함된다면 이러한 과정은 이더리움의 블록 가스 제한을 넘길 수 있습니다.

트루빗의 Github wiki를 참고하면 이를 위해 단일 명령어가 내포하는 low-level에서의 작업들을 여러 phase로 구분함으로써 더욱 작은 단위로 쪼개어 해결하려고 하고 있습니다.

5. WebAssembly라는 조건으로 인한 진입 장벽

Dapp 입장에서 트루빗으로 특정 연산을 해결하고 싶다면, 기존에 짜두었던 Solidity 코드를 WebAssembly용 언어로 다시 작성해야만 합니다. 이것 또한 트루빗의 사용성을 저해하는 장애물이 될 것입니다.

트루빗은 웹어셈블리가 많은 대기업들(구글, 애플, 마이크로소프트 등)의 후원을 받고 있는 언어이고 이더리움 역시 EVM의 다음 버전으로 eWASM을 개발하고 있기 때문에 장래를 보고 선택한 것으로 보입니다. 왜 트루빗이 웹어셈블리를 채택하였는가에 대한 답은 트루빗의 Github wiki에 명시되어 있습니다.

정리

트루빗은 기존에 이더리움이 갖고 있는 블록 가스 제한의 병목(bottleneck)을 오프체인으로 해결하고자 하는 시도입니다. 오프체인으로 연산을 처리하면서도 결과값에 대한 검증 장치를 온체인으로 두었다는 점에서 재미있는 시도라고 할 수 있습니다. 별도로 개발되고 있는 웹어셈블리 인터프리터를 EVM 위에 구현한다는 것이 쉬운 작업은 아닐 것입니다. 하지만 레이어별로 코드 구현도 활발히 이루어지고 있는 것으로 보입니다. 또한 이전부터 관련된 연구를 진행하며 여러 페이퍼를 내고 있는 Jason Teutsch이 이끌고 있는 팀인 만큼 학술적으로 접근하고 있는 모습입니다.

다만, 아쉬운 점은 트루빗이 이를 통해 해결하고자 하는 현실적인 타겟 도메인이 모호하다는 점입니다. 이들이 제시하는 현실 적용의 예시로서 백서에서는 골렘(Golem)을, Medium에서는 아라곤(Aragon)을 언급하고 있는데, 우선 두 프로젝트에서 요구하는 연산량의 차이는 엄청납니다. 필자가 바라보기에는 골렘과 같은 분산형 슈퍼컴퓨팅 정도의 연산을 이더리움 스마트 컨트랙트를 통해 검증 게임을 진행하면서 결과적으로 온체인 WASM 인터프리터에서 검증을 진행한다는 것은 무리가 있어보입니다. 따라서 아라곤 정도의 예시가 적합하다고 생각됩니다.

이와 더불어, 사실 트루빗은 연산에 대한 솔루션이라기 보다는 정확히는 연산 검증에 대한 솔루션이라고 보는 것이 맞는 듯합니다. 왜냐하면 트루빗에 task를 맡긴다고 해서 항상 정확한 결과값을 적시에 제공받을 수 있다는 보장이 없습니다. 오히려 틀린 결과값을 받지 않을 것이라는 보장은 할 수 있습니다. 따라서 다음과 같은 시나리오가 현실적입니다.

Dapp 운영자가 특정 연산을 수행하고자 하지만 이더리움 블록 가스 제한을 넘겨 온체인 스마트 컨트랙트에서 실행할 수 없습니다. 이 때 로컬에서 계산한 결과값을 반영할 수 있지만, 이로써는 탈중앙화 커뮤니티의 동의를 받을 수 없습니다. 따라서 이 연산을 WASM으로 구현하여 트루빗 컨트랙트에 넘깁니다. 그리고는 자신이 트루빗 네트워크의 문제 해결자(Solver)로 참여하여 연산 결과를 제출합니다. 자신의 연산 결과를 누구든 와서 검증해달라고 요청하며 이 연산 결과가 틀리지 않았음을 보장받습니다. 이 결과값을 Dapp에 적용합니다.

명확한 타겟 도메인을 제시하며 솔루션을 완성한다면 당장의 이더리움을 개선할 수 있으리라 기대됩니다. 더불어 자체적인 토큰을 발행하는 방안(참고: Medium)이나 시스템 설계를 간소화하는 방안(pairwise verification game) 등에 대해 연구를 병행하고 있어 추가적인 결과 발표가 기대됩니다.

다음 글 Truebit Code Review에서는 Github의 코드를 바탕으로 이번 글에서 짚었던 주요 지점들이 어떻게 구현되고 있는지, 백서 설계 때와 어떻게 변했는지 등에 대해 조금 더 자세히 살펴보도록 하겠습니다.

--

--