Computation challenge on Tokamak

토카막 네트워크 플라즈마 오퍼레이터(마이너)의 이중지불을 막는 방법

Jake Song
Tokamak Network
23 min readSep 17, 2019

--

컴퓨테이션 챌린지(Computation Challenge, 이하 CP challenge)란?

CP challenge란 토카막 체인에 배포된 컨트랙트를 검증하여 오퍼레이터의 연산이 올바로 되었는지 검증하는 작업입니다. 챌린저는 풀노드를 운영하는 유저로서 잘 못된 연산을 했다고 판단한 경우 일정 금액을 예치하고 오퍼레이터에게 연산 결과를 요청합니다. 이더리움에 배포된 검증 컨트랙트에 의해 승자와 패자가 가려지게 되고 패자의 예치금은 삭감합니다. 이로써 오퍼레이터가 올바른 연산을 하도록 견제할 수 있습니다.

토카막 체인은 왜 CP challenge가 필요한가?

토카막 체인은 플라즈마 체인으로서 이더리움의 탈중앙성에 의존한 프라이빗 체인이라고 할 수 있습니다. 토카막 체인은 여러 합의 방식이 가능하지만 오퍼레이터가 하나인 POA 합의방식으로 성능을 극대화할 수 있습니다. 이 글에서는 POA 합의기준으로 CP challenge가 왜 필요한지, 어떻게 동작하는 지에 대해 이야기하겠습니다.

CP challenge를 설명하기에 앞서 토카막 체인은 왜 이러한 별도의 검증과정이필요한지에 대해 알아 볼 필요가 있습니다. 왜 필요한지 이해하기 위해선 우선 이더리움과 토카막 체인의 차이점에 대해서 알아야 합니다.

먼저 이더리움에서 어떻게 검증작업이 이뤄지는지 알아보겠습니다.

이더리움은 진입장벽이 없는 퍼블릭 체인입니다. 이론적으로 누구나 네트워크에 참여하여 거래(transaction)를 포함한 블록을 마이닝하고 이더리움 가상머신(EVM)을 통해 연산후 상태(state)에 반영할 수 있습니다. 그 다음 블록을 다른 노드(Full Node)에게 전파하게 됩니다. 거래가 담긴 블록을 전파받은 노드는 거래를 이더리움 가상머신(EVM)으로 다시 연산해보면서 올바른 거래인지 검증하게 됩니다.

만약 누군가 고의적으로 클라이언트를 수정하여 잘 못된 연산을 하고 이에 따라 잘 못된 상태값을 반영 했다면 어떻게 될까요? 잘못된 거래를 전파받은 다른 노드는 연산을 하여 잘못된 거래가 있다는 것을 알게 되고 그 블록을 받지 않게 될 것입니다. 그리고 다른 노드가 올바른 거래가 포함된 블록을 보내주면 블록을 받을 것입니다. 결국 이더리움에서는 잘 못된 연산을 하여 블록을 전파한다고 해도 받아들여지지 않습니다.

토카막 체인의 검증 과정을 살펴보겠습니다. 오퍼레이터는 블록에 거래를 담아 가상머신으로 연산한 후 마이닝하여 다른 사용자 노드(Full Node)에 전파하게 됩니다. 사용자 노드는 전파받은 거래를 다시 가상머신으로 연산하여 올바른 거래인지 검증하게 되죠. 여기까지는 이더리움과 같습니다. 하지만 이더리움과 달리 토카막 체인은 오퍼레이터만 거래를 담아 블록을 마이닝하고 이더리움에 제출합니다. 예외인 경우도 있습니다. 데이터 가용성(Data Availability) 문제가 있는 경우 사용자가 직접 제출하는 경우가 있으나 여기서는 데이터 가용성(Data Availability) 문제는 논외로 하겠습니다. 토카막에서 어떻게 데이터 가용성(Data Availability) 문제를 해결하는 지에 관해서는 이 글(춤추는 철학자의 문제, 검증 게임 그리고 사이드 체인과 데이터 가용성)을 참고해주세요. 기술적으로 보다 깊게 이해하고 싶으신 분들은 토카막 Tech Paper를 보시면 될 것 같습니다.

예를 들어 오퍼레이터가 잘 못된 연산을 하고 다른 사용자 노드에 전파한다면 사용자 노드는 검증을 하여 유효하지 않은 거래라는 것을 알 수 있을 것입니다. 하지만 토카막 체인에서는 오퍼레이터만 블록을 마이닝하고 사용자 노드에 전파합니다. 따라서오퍼레이터가 유효하지 않은 거래를 포함해 블록을 마이닝해도 사용자 노드는 그 블록을 받아 들일 수 밖에 없습니다. 오퍼레이터가 절대적인 권한을 갖고 있는 셈입니다. 이러한 오퍼레이터를 견제할 장치가 필요했고 CP challenge를 구현하는 동기가 되었습니다.

CP challenge의 작동방식

오퍼레이터와 챌린저 중에 누가 올바른 연산을 했는지 어떻게 알 수 있을 까요? 이더리움에서 검증 컨트랙트를 배포하여 챌린지를 신청한 거래를 실행하여 어떤 결과가 나오는지 확인해보면 알 수 있을 것입니다. 검증 컨트랙트를 통해 승자와 패자가 결정되고 결과를 체인에 기록하도록 합니다. 이런 방식이라면 승자와 패자 그리고 토카막 플라즈마 네트워크에 있는 다른 사용자들도 검증 결과를 수용하고 신뢰할 수 있을 것입니다.

solEVM!

말씀드렸듯이 검증은 스마트 컨트랙트에서 이뤄지게 됩니다. 따라서 스마트 컨트랙트에서 동작하는 이더리움 가상머신이 필요합니다. solEVM은 solidity로 이더리움 가상머신을 구현한 것으로 스마트 컨트랙트에서 이더리움 가상머신(EVM)을 사용하는 것을 가능하게 해줍니다. (solEVM github) solEVM을 통해 거래를 연산하고 그 결과값을 비교하여 누가 승자인지 패자인지 가릴 수 있게 됩니다. 하지만 고려해야 할 점이 하나 있습니다. 이더리움은 자원이 무한하지 않습니다. 연산부담이 커질수록 거래 수수료(gas)를 많이 부담 해야만 합니다. 거래의 전 과정을 온 체인에서 연산한다면 거래 수수료(gas)의 부담은 커질 수 밖에 없고 블록의 블록마다 사용할 수 있는 gas량(gas limit)을 초과하여 블록에 포함 되지 못 할수도 있습니다. 따라서 gas 부담을 덜기 위해 오프체인에서 opcode별로 단계를 나누고 opcode 마다 가상머신의 상태값을 만들게 됩니다. 그 다음 질의 / 응답과정을 거쳐 서로의 응답값이 다른 opcode를 찾아 냅니다. 그 다음 opcode의 전 상태값을 검증 컨트랙트에 올려 단 한개의 opcode만 실행하여 결과값을 산출합니다. 그 다음 오퍼레이터와 챌린저의 제출값을 결과값과 비교하여 승자와 패자를 가리게 되는 것입니다.

Deep dive into CP challenge with code!

토카막 프로토콜의 CP challenge 구현체는 LeapDAO의 solEVM-enforcer 구현체를 fork하여 개발하고 있습니다.(solEVM-enforcer github) Leapdao는 Plasma를 구현하는 프로젝트를 진행하고 있으며 Tokamak과 마찬가지로 solEVM을 기반한 Computation Challenge를 개발하고 있습니다. 더 많은 정보는 LeapDAO 홈페이지를 참고하시면 좋을 것 같습니다.

코드를 통해 보다 구체적으로 설명해보겠습니다. 작동환경에 따라 오프체인 / 온체인 두가지 경우로 나눠 보겠습니다.

오프체인에서는 EVMRuntime.js, HydratedRuntime.js, Merkelizer.js 파일이 핵심적인 역할을 합니다. 하나씩 살펴보겠습니다.

  • EVMRuntime.js

로컬환경에서 사용할 수 있는 가상머신(Virtual Machine)이며 opcode에 따라 연산을 수행합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/EVMRuntime.js#L94-L139

Line 4: 입력받은 바이트 코드에서 opcode를 불러옵니다.
Line 15 ~ 22: stack underflow, overflow를 체크합니다.
Line 25 ~ 29: gas가 충분히 있는지 체크합니다.
Line 35 ~ 38: opcode 함수를 실행합니다.

opcode 함수 예

https://github.com/Onther-Tech/solEVM/blob/compact/utils/EVMRuntime.js#L258-L281

Line 7 ~ 8: stack을 가져와 변수 a, b에 저장합니다.
Line 11: a, b의 합을 다시 stack에 넣습니다. 뒤에 mod는 모듈러스(modulus)로 256bit로 수의 범위를 제한한 것입니다. 256bit 범위를 초과하면 다시 0부터 시작합니다.

  • HydratedRuntime.js

각 opcode 구간별 stack, memory, pc, returnData등 가상머신의 상태를 나타내는 값을 산출합니다. 앞으로 이 값을 가상머신 상태값이라고 하겠습니다. 가상머신 상태값은 오퍼레이터와 챌린저가 검증 컨트랙트에 제출하게 되는 값입니다. 앞으로 컨트랙트에 제출된 값을 제출값이라고 하겠습니다.

HydratedRuntime.js의 다른 역할은 컨트랙트의 저장소(stroage) 데이터 및 입력값을 compact하게 만드는 것입니다. 여기서 compact의 의미는 opcode가 필요한 가상머신 상태값만 올려 온체인에서 연산부담과 가스 비용을 최소화하는 것입니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/HydratedRuntime.js#L81-L99

Line 3: 가상머신의 stack입니다. 배열 형식입니다.
Line 6: 바이트 코드의 몇 번째 opcode인지 알려줍니다.
Line 11: storage 데이터를 나타냅니다.

  • Merkelizer.js

HydratedRuntime.js를 통해 opcode 별로 가상머신 상태값이 주어지게 되는데, 이 값을 가지고 머클트리(Merkle Tree)를 만들게 됩니다. 챌린지 할 때 머클트리의 루트해쉬(Root Hash)를 제출하며 루트해쉬(Root Hash)가 다르다면 질의 / 응답 단계로 들어가게 됩니다. 또한 머클트리의 각 Path는 질의 / 응답 단계에서 검증 컨트랙트에 제출값이 됩니다. Leaf Node에는 각각 가상머신 상태값이 저장되어 있습니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/Merkelizer.js#L205-L225

Line 1: stateHash값을 만듭니다. stateHash는 가상머신 상태값을 해쉬(hash)한 것으로 질의 / 응답 과정에서 검증 컨트랙트에 제출할 값입니다.
Line 2 ~ 20: opcode 구간별 가상머신 상태값을 Leaf Node로 만듭니다. Leaf Node에는 stateHash, stackHash, memHash, StorageHash이 저장됩니다.

  • proofHelper.js

질의 / 응답 단계가 끝나고 최종 검증할 때 필요한 입력값을 만들어 주는 역할을 합니다.

proof와 execution input를 제출하게 됩니다. proof를 제출하는 이유는 검증하고자 하는 opcode를 실행할 때 사용하는 부분이 아닐 경우 미리 해쉬(hash)하여 데이터 크기를 줄인 것입니다. 예를 들어 stack이 5개가 있다고 했을 때 opcode ADD를 검증한다고 해보겠습니다. ADD는 5개중 위에 있는 stack 2개만 사용할 것입니다. 나머지 3개는 입력값으로 넣을 필요가 없을 것입니다. 따라서 proof로 나머지 3개를 미리 해쉬하여 입력값으로 넣어 두게 됩니다. execution input은 opcode가 실행되기전 가상머신 상태값이며 이 상태값으로 연산하여 결과값, 즉 opcode가 실행된 후의 상태값을 산출합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/ProofHelper.js#L69-L84

Line 2: proofs 값입니다.
Line 4 ~ 13: 최종 검증시 제출할 가상머신 상태값입니다.
Line 14: 이미 할당되어 있는 storage slot에 값만 변경하는 지 여부를 확인합니다. storage는 새로운 슬롯에 값을 저장하는 것과 기존 슬롯에 값을 변경하는 gas cost 차이가 있기 때문에 reset 여부를 확인해야 합니다.

온체인에서는 EVMRuntime.sol, Verifier.sol, Merklizer.sol 파일이 핵심적인 역할을 합니다.

  • Verifier.sol
    질의 / 응답을 통해 다른 결과값을 가진 opcode를 찾아냅니다.
    respond 함수를 통해오퍼레이터와 챌린저는 각자의 상태값(Merkle Path)을 제출합니다.
https://github.com/Onther-Tech/solEVM/blob/compact/contracts/VerifierStorage.sol#L122-L149

Line 9: leaf node까지 도달하였는지 체크합니다.
Line 12 ~ 18: 제출한 Merkle Path의 노드들의 해쉬값이 올바른지 체크합니다. Merkle Path의 왼쪽 노드와 오른쪽 노드를 해쉬한 값이 Merkle Path의 해쉬값과 일치한다면 올바른 해쉬값을 제출했다고 볼 수 있습니다.
Line 21 ~ 24: solver, 즉 오퍼레이터가 응답했음을 기록하고 제출한 Merkle Path 해쉬를 저장합니다.
Line 27 ~ 30: 챌린저의 응답했음을 기록하고 제출한 Merkle Path 해쉬를 저장합니다.

질의 / 응답을 통해 다른 결과값을 가진 opcode를 찾으면 submitProof 함수를 호출하여 온체인에서 최종 검증을 합니다. EVMRuntime.sol의 _run함수를 호출하게 됩니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/VerifierStorage.sol#L227-L239

Line 6 ~ 7, 10: 입력값으로 받은 배열 형태의 stack을 solEVM이 실행할 수 있게 구조체(struct) 형태로 변환합니다.
Line 13: EVMRuntime.sol의 _run 함수를 실행합니다.

  • EVMRuntime.sol

Verifier.sol에서 전달받은 가상머신 상태값으로 solEVM을 실행합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/EVMRuntimeStorage.sol#L50-L70

Line 10: 에러 및 기타 체크 후 바이트 코드(byte code)에서 pc에 따라 opcode를 가져옵니다.
Line 11: opcodeHandler를 정의하여 opcode마다 별도의 함수를 사용할 수 있게 합니다.
Line 14 ~ 18, 19 ~ 23: opcode에 맞는 함수를 실행합니다.

EVMRuntime.sol에는 opcode별 로직이 정의되어 있어 opcode에 따라 가상 머신을 실행합니다. opcode가 정의되어 있는 코드입니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/EVMRuntimeStorage.sol#L1482-L1508
  • Merklizer.sol

승자를 결정할 최종 결과값은 가상머신 상태값을 해싱(hashing)한 값입니다. Merklizer.sol에는 가상머신 상태값을 해싱(hashing)하여 최종 결과값을 만듭니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/MerkelizerStorage.slb#L45-L87

Line 1 ~ 3: storageHash값을 구합니다.
Line 28 : 검증을 위해 가상머신 상태값을 해쉬(hash)합니다.

테스트 코드를 통해 보다 구체적으로 살펴보겠습니다.

ERC20 컨트랙트를 검증하는 테스트 코드입니다.

https://github.com/Onther-Tech/solEVM/blob/compact/test/fixtures/disputeERC20.js#L11-L43

Line 1: code는 컨트랙트의 바이트 코드(byte code)입니다.
Line 2: data는 calldata로 바이트 코드의 어떤 부분을 실행할 지 결정합니다.
Line 13: tStorage는 검증 이전의 storage 값입니다. [slot, value, slot, value] 형식으로 되어 있습니다. * storage의 구조는 slot과 value로 이루어져 있습니다. slot은 인덱스에 해당하며 value는 해당 인덱스에 저장되어 있는 값입니다.
Line 23: 새 HydratedRuntime 객체를 만듭니다.
Line 27: 각 opcode 별 가상머신 상태값을 만듭니다.
Line 29: 각 opcode 별 가상머신 상태값으로 머클트리(Merkle Tree)를 만듭니다.
Line 33 ~ 35: 오퍼레이터와 챌린저가 똑같은 결과를 제출한 경우 오퍼레이터가 승리하는 테스트 코드입니다.

여기서 고려할 점은 이더리움에서 검증하기 위해 바이트 코드(byte code)를 제출하여야 한다는 것입니다. 만약 오퍼레이터와 챌린저가 서로 다른 바이트 코드(byte code)를 제출 했다면 누구의 바이트 코드(byte code)가 올바른 코드라고 할 수 있을 까요? 따라서 바이트 코드(byte code)검증을 위해 오퍼레이터와 챌린저는 바이트 코드(byte code)를 머클 트리(Merkle Tree)로 만들고 검증할 opcode에 대한 Merkle Proof를 최종 검증시 제출하게 됩니다.

callback 함수에 인자로 전달되어 test폴더의 disputeERC20.js에 있는 doGame을 실행하게 됩니다. doGame을 실행하게 되면 submitRound를 실행하게 되고 머클 트리의 루트 해쉬(Root Hash)부터 순차적으로 Merkle Path 값을 비교하며 Leaf Node에 도달하게 됩니다.

https://github.com/Onther-Tech/solEVM/blob/compact/test/contracts/disputeERC20.js#L201-L217

Line 3 ~ 17: callback으로 사용되는 함수입니다.
Line 12: callback함수로 넘겨진 인자를 받아 doGame함수를 실행합니다.

Leaf Node에 도달할 때까지 Merkle Path를 비교하는 과정을 반복합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/ExecutionPoker.js#L209-L264

Line 5 ~ 12: Leaf Node까지 도달했는지 체크하고 Leaf Node라면 온체인에서 최종 검증을 하는 submitProof 함수를 호출합니다.
Line 17: targetPath는 오퍼레이터와 챌린저가 서로 다른 해쉬값입니다. 다음 응답 차례에 오퍼레이터와 챌린저는 targetPath의 하위 왼쪽 노드와 오른쪽 노드의 해쉬값을 제출합니다.
Line 18: 현재 응답단계에서 제출한 Merkle Path입니다.
Line 19: nextPath는 targetPath의 하위 왼쪽 노드와 오른쪽 노드의 해쉬값을 저장하고 있습니다.
Line 29 ~ 33: targetPath가 현재 path의 하위 왼쪽 노드의 해쉬값이라면 왼쪽 노드를 따라 내려갑니다.
Line 34 ~ 39: targetPath가 현재 path의 하위 오른쪽 노드의 해쉬값이라면 왼쪽 노드를 따라 내려갑니다.
Line 58 ~ 66: 다음 응답 차례에 오퍼레이터와 챌린저는 서로 다른 해쉬값(targetPath)의 하위 왼쪽 노드와 오른쪽 노드의 해쉬값을 제출합니다.

응답 과정을 거쳐 Leaf Node에 도달하면 submitProof를 실행하여 최종 검증을 하게 됩니다.

구현과정

LeapDAO의 플라즈마 구현체(Plasma Leap)는 UTXO 모델으로 이더리움의 상태(state)를 갖고 있지 않습니다. LeapDAO의 solEVM-enforcer는 Plasma Leap 체인에 배포된 컨트랙트를 검증하는 것이기 때문에 이더리움의 상태(state)를 다루는 opcode는 구현되어 있지 않습니다. 따라서 현재 중점적으로 구현하는 부분은 상태(state)를 다루는 opcode를 지원하는 것입니다. 온체인 / 오프체인으로 나눠 구현하게 됩니다.

스토리지 데이터(storage data)
토카막은 이더리움과 같이 상태(state)를 갖고 있기 때문에 스토리지 데이터(storage data)를 다루는 opcode를 구현해야 합니다. 먼저 온체인의 경우를 보면 주로 앞서 말씀드린 EVMRuntime.sol, Verifier.sol, Merklizer.sol를 수정합니다.

opcode SSTORE, SLOAD를 구현한 코드입니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/EVMRuntimeStorage.sol#L1482-L1508

Line 2: stack의 값을 가져와 변수 addr에 할당합니다.
Line 3: addr은 stroage의 slot을 의미합니다. slot에 저장되어 있는 value를 불러옵니다.
Line 4: stack에 value를 올립니다.
Line 9: stack에서 값을 가져와 변수 addr에 할당합니다.
Line 10: stack에서 값을 가져와 변수 val에 할당합니다.
Line 14 ~ 18: storage에 새로 값을 할당하는 지 기존 값을 변경하는 지에 따라 가스 소모량을 정합니다.
Line 31: storage에 slot, value를 저장합니다

Verifier.sol에서 배열 형식으로 받은 입력값을 컨트랙트에서 사용할 수 있도록 구조체(struct)로 바꾸는 작업이 필요합니다. 스토리지(storage)의 입력값을 구조체로 바꾸는 로직을 넣어줍니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/VerifierStorage.sol#L227-L239

Line 11: 입력값으로 받은 배열 형태의 storage data를 solEVM이 사용할 수 있도록 구조체(struct)로 변환합니다.

Merklizer.sol에서 최종 결과값을 해쉬(hash)할 때 스토리지(storage)도 추가하여 해쉬(hash)합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/contracts/MerkelizerStorage.slb#L45-L87

Line 1 ~ 3: storageHash값을 구합니다.
Line 33 : 가상머신 상태값을 해쉬(hash)할 때 storageHash를 포함합니다.

오프체인에서는 EVMRuntime.js, HydratedRuntime.js, Merkelizer.js에 로직을 추가합니다.

EVMRuntime.js에 opcode를 추가합니다. SLOAD를 구현한 코드입니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/EVMRuntime.js#L720-L745

Line 5: stack에서 값을 가져와 변수 addr에 할당합니다. addr은 storage slot입니다.
Line 12: 오프체인에서 storage의 자료구조는 배열이며 [slot, value, slot, vlaue,…] 형식입니다. 반복문을 실행해 일치하는 slot을 찾고 value를 변수 val에 할당합니다.
Line 20 ~ 25: slot에 value가 저장되어 있어 있지 않다면 value는 0입니다. slot에 value가 저장되어 있다면 value를 stack에 올립니다.

SSTORE를 구현한 코드입니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/EVMRuntime.js#L747-L778

Line 2 ~ 3: stack에서 값을 가져와 변수 addr, val에 할당합니다. addr은 storage slot, val은 storage value입니다.
Line 26 ~ 32: 새로 storage를 저장하는 경우 가스 20000을 차감하고 storage 배열에 추가하고 storage를 변경하는 경우 가스 5000을 차감하고 storage 배열의 해당 slot의 value를 변경합니다.

HydratedRuntime.js에 가상머신 상태값필드에 스토리지(storage)를 추가합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/HydratedRuntime.js#L101-L120

Line 6 ~10 : opcode가 SSTORE인 경우 가상머신 상태값에 storage를 추가하고 가상머신의 runState에서 Storage를 새로 저장하는지 변경하는 지 여부를 가져옵니다.
Line 13 ~16: opcode가 SLOAD인 경우 가상머신 상태값에 storage를 추가합니다.

Merkelizer.js에 스토리지 데이터(storage data) 해쉬(hash)함수를 구현합니다.

https://github.com/Onther-Tech/solEVM/blob/compact/utils/Merkelizer.js#L96-L143

Line 1 ~ 6: storageHash값을 구합니다.
Line 9 ~ 51: stateHash값을 구할 때 storageHash값을 추가합니다.

현재 구현 이슈

검증 이전에 컨트랙트 스토리지 데이터가 있는 경우

검증 이전에 컨트랙트 스토리지 데이터(storage data)가 있는 경우 여러 컨트랙트를 call 할 경우 스토리지 데이터(storage data)를 어떻게 가져올 것인지에 대한 이슈가 있습니다. 검증이전의 스토리지 데이터(storage data)는 최종 검증시 입력값으로 가져와야 합니다. 만약 다른 컨트랙트를 call하지 않는 다면 앞서 설명드린 최종 검증 시 바이트 코드(byte code)를 입력값으로 한 것과 같이 스토리지 데이터(storage data), 스토리지Merkle Root Hash, 스토리지 데이터(storage data)에 대한 Merkle Proof를 같이 제출하면 됩니다. 하지만 여러 컨트랙트를 call하는 경우 최종 검증시 어떻게 효율적으로 입력값을 가져 올 지에 대한 연구가 더 필요합니다.

결론

CP challenge는 토카막 체인에서 오퍼레이터가 잘 못된 연산을 하지 못 하도록 견제하는 수단입니다. 만약 오퍼레이터가 잘 못된 연산을 한다면 사용자는 챌린지를 신청해 이더리움에 배포되어 있는 검증 컨트랙트로 누가 옳고 그른지 가릴 수 있습니다. 검증과정은 오프체인에서 opcode 별 가상머신 상태값을 만들고 질의 / 응답을 거쳐 서로 다른 opcode를 찾아냅니다. 온체인에서 opcode 하나를 실행하여 질의 / 응답 과정에서 제출한 가상머신 상태값과 비교하여 승자와 패자를 가리게 됩니다. 토카막은 이더리움과 같이 상태(state)를 가지고 있으므로 CP challenge는 상태(state)를 다룰 수 있는 opcode를 구현해야 합니다. 검증 이전의 스토리지 데이터(storage data)가 있는 경우 효율적으로 검증하는 것은 연구가 더 필요한 부분입니다.

참고할 자료, 영상 등

다음의 국문자료와 영상은 토카막과 CP challenge를 이해하는데 많은 도움을 줍니다.

--

--

Jake Song
Tokamak Network

blockchain engineer, Onther Inc developer, interested in developer’s culture