이해의 편의를 돕기 위한 테조스 코인 백서 번역본(Tezos Whitepaper)으로, 정확한 내용은 테조스 공식 홈페이지에 게시된 백서 원문을 참고해주시길 바랍니다.
요약
우리는 범용적이고 자가 수정이 가능한 암호 원장인 테조스(XTZ)를 소개합니다. 테조스는 어떤 블록체인 기반 원장도 구현할 수 있습니다. 일반 블록체인의 작업은 순수 함수형 모듈로 구현되어 네트워크 작업을 담당하는 쉘로 추상화됩니다. 비트코인, 이더리움, 크립토노트 등은 모두 테조스 내에서 적절한 인터페이스를 네트워크 계층에 구현함으로써 표현될 수 있습니다.
가장 중요하게, 테조스는 메타 업그레이드를 지원합니다: 프로토콜은 자체 코드를 수정함으로써 진화할 수 있습니다. 이를 달성하기 위해, 테조스는 이해관계자들이 프로토콜에 대한 수정안을 승인하는 절차를 정의하는 초기 프로토콜로 시작합니다. 이는 투표 절차 자체에 대한 수정안을 포함합니다. 이는 철학자 피터 서버의 노믹[3]과 유사한데, 이는 완전히 자기 성찰적인 규칙 세트를 중심으로 구축된 게임입니다.
또한, 테조스의 초기 프로토콜은 순수 지분 증명 시스템을 기반으로 하며, 튜링 완전 스마트 계약을 지원합니다. 테조스는 속도, 모호하지 않은 문법과 의미, 그리고 정확성의 형식적 증명에 적합한 생태계를 제공하는 강력한 함수형 프로그래밍 언어인 OCaml로 구현됩니다.
이 논문의 나머지 부분에서는 비트코인 프로토콜과 기본 암호학적 기본 요소에 대한 지식이 있다고 가정합니다.
서론
이 논문의 첫 번째 부분에서는 추상 블록체인의 개념과 자가 수정 가능한 암호 원장의 구현에 대해 논의할 것입니다. 두 번째 부분에서는 제안된 초기 프로토콜에 대해 설명할 것입니다.
자가 수정 암호 원장 (Self-amending cryptoledger)
블록체인 프로토콜은 세 가지 독립적인 프로토콜로 분해될 수 있습니다.
- 네트워크 프로토콜 (Network Protocol): 블록을 발견하고 거래를 방송합니다.
- 거래 프로토콜 (Transaction Protocol): 거래가 유효한 것을 명시합니다.
- 합의 프로토콜 (Consensus Protocol): 유일한 체인 주변에 합의를 형성합니다.
테조스(Tezos)는 일반적인 네트워크 쉘을 구현합니다. 이 쉘은 거래 프로토콜과 합의 프로토콜에 대해 무지합니다. 우리는 거래 프로토콜과 합의 프로토콜을 함께 “블록체인 프로토콜”이라고 합니다. 우리는 먼저 블록체인 프로토콜의 수학적 표현을 제공한 다음, 테조스에서의 몇 가지 구현 선택 사항을 설명할 것입니다.
수학적 표현
블록체인 프로토콜은 근본적으로 전역 상태의 동시 변화를 모나딕 구현입니다. 이는 “블록”을 이 전역 상태에 작용하는 연산자로 정의함으로써 달성됩니다. 초기 상태에 작용하는 블록의 자유 모노이드는 트리 구조를 형성합니다. 전역적이고 규범적인 상태는 지정된 정렬에 대한 최소 리프로 정의됩니다. 이는 다음과 같은 추상적 표현을 제안합니다.
- (S, ≤)를 가능한 상태들의 완전히 정렬된, 셀 수 있는 집합으로 합시다.
- ⊘ ∈/ S가 특별하고 유효하지 않은 상태를 나타내게 합시다.
- B ⊂ S S∪{⊘}를 블록의 집합으로 합시다. 유효한 블록의 집합은 B ∩ S^S입니다. S 상의 전체 순서는 ∀s ∈ S에 대해 ⊘ < s로 확장되어, 블록 트리에서 어떤 리프가 규범적인 것으로 간주되는지 결정합니다. B 안의 블록들은 상태에 작용하는 연산자로 간주됩니다.
S 상의 전체 순서는 ∀s ∈ S에 대해, ⊘ < s로 확장됩니다. 이 순서는 블록 트리의 어떤 리프가 규범적인 것으로 간주될지를 결정합니다. B 안의 블록들은 상태에 작용하는 연산자로 간주됩니다.
결론적으로, 어떤 블록체인 프로토콜(비트코인, 라이트코인, 피어코인, 이더리움, 크립토노트 등)이든 다음 튜플로 완전히 결정될 수 있습니다.
(S, ≤, ⊘, B ⊂ S^(S∪{⊘}))
네트워크 쉘
이 형식적인 수학적 설명은 블록 트리를 어떻게 구축하는지 알려주지 않습니다. 이는 네트워크 쉘의 역할로, 이는 가십 네트워크와 프로토콜 사이의 인터페이스 역할을 합니다.
네트워크 쉘은 클라이언트에게 알려진 최고의 체인을 유지함으로써 작동합니다. 이는 세 가지 유형의 객체를 인지합니다. 첫 번째 두 가지는 거래와 블록으로, 유효하다고 판단될 경우에만 네트워크를 통해 전파됩니다. 세 번째는 프로토콜로, 기존 프로토콜을 수정하는데 사용되는 OCaml 모듈입니다. 이들은 나중에 더 자세히 설명될 것입니다. 지금은 거래와 블록에 초점을 맞추겠습니다.
네트워크 쉘의 가장 어려운 부분은 노드를 서비스 거부 공격으로부터 보호하는 것입니다.
클락 (Clock)
모든 블록은 네트워크 쉘에게 보이는 타임스탬프를 가지고 있습니다. 미래에서 온 것처럼 보이는 블록은 시스템 시간과 몇 분 이내인 경우 버퍼링되고 그렇지 않은 경우 거부됩니다. 프로토콜 설계는 클라이언트의 합리적인 시계 드리프트를 용인하고 타임스탬프가 위조될 수 있다고 가정해야 합니다.
체인 선택 알고리즘
쉘은 블록의 전체 트리가 아닌 단일 체인을 유지합니다. 이 체인은 클라이언트가 엄격하게 더 나은 체인을 인지하게 될 경우에만 덮어 씌워집니다. 트리를 유지하는 것이 네트워크 통신 측면에서 더 절약일 수 있지만, 공격자가 점수는 낮지만 유효한 다수의 포크를 생성하는 서비스 거부 공격에 취약할 수 있습니다. 그럼에도 불구하고, 노드가 주어진 체인의 점수에 대해 거짓말을 할 가능성이 남아 있으며, 클라이언트는 잠재적으로 많은 수의 블록을 처리한 후에야 이 거짓말을 발견할 수 있습니다. 그러나, 이러한 노드는 이후에 무시될 수 있습니다. 다행히 점수가 낮은 체인은 블록 생성률이 낮다는 특성을 가질 수 있습니다. 따라서 클라이언트는 약한 포크의 몇 블록만을 고려한 후에 발표된 점수가 거짓말이었다는 결론을 내릴 것입니다.
네트워크 수준 방어
또한, 쉘은 “방어적”입니다. 다양한 IP 범위에 걸쳐 많은 피어에 연결을 시도합니다. 연결이 끊긴 피어를 감지하고 악의적인 노드를 차단합니다. 특정 서비스 거부 공격에 대한 보호를 위해, 프로토콜은 쉘에게 블록과 거래의 크기에 대한 상황에 따른 제한을 제공합니다.
기능적 표현
체인 검증하기
우리의 추상 블록체인 구조의 거의 모든 일반성을 다음과 같은 OCaml 타입으로 효율적으로 포착할 수 있습니다. 우선, 블록 헤더는 다음과 같이 정의됩니다:
ocamlCopy codetype raw_block_header = {
pred: Block_hash.t;
header: Bytes.t;
operations: Operation_hash.t list;
timestamp: float;
}
우리는 헤더 필드를 고의적으로 더 강하게 타입하지 않아서 임의의 내용을 대표할 수 있게 합니다. 그러나, 쉘의 작동에 필요한 필드들은 타입을 지정합니다. 이들은 선행 블록의 해시, 작업 해시 목록, 그리고 타임스탬프를 포함합니다. 실제로, 블록에 포함된 작업은 네트워크 수준에서 블록과 함께 전송됩니다. 작업 자체는 임의의 블롭으로 표현됩니다.
ocamlCopy codetype raw_operation = Bytes.t
상태는 디스크 기반의 변경 불가능한 키-값 저장소를 캡슐화하는 컨텍스트 모듈의 도움으로 표현됩니다. 키-값 저장소의 구조는 다양하고 넓은 범위의 상태를 효율적으로 표현할 수 있게 해줍니다.
ocamlCopy codemodule Context = sig
type t
type key = string list
val get: t -> key -> Bytes.t option Lwt.t
val set: t -> key -> Bytes.t -> t Lwt.t
val del: t -> key -> t Lwt.t
(*...*)
end
디스크 작업에 대한 차단을 피하기 위해, 함수들은 비동기 모나드[4]를 사용합니다. 컨텍스트에 대한 작업은 순수하게 함수적입니다: get
은 예외를 던지기보다는 옵션 모나드를 사용하며, set
과 del
은 모두 새로운 컨텍스트를 반환합니다. 컨텍스트 모듈은 메모리 캐싱과 디스크 저장을 결합하여 변경 불가능한 저장소의 모습을 효율적으로 제공합니다.
이제 임의의 블록체인 프로토콜의 모듈 타입을 정의할 수 있습니다:
ocamlCopy codetype score = Bytes.t list
module type PROTOCOL = sig
type operation
val parse_block_header : raw_block_header -> block_header option
val parse_operation : Bytes.t -> operation option
val apply :
Context.t ->
block_header option ->
(Operation_hash.t * operation) list ->
Context.t option Lwt.t
val score : Context.t -> score Lwt.t
(*...*)
end
우리는 더 이상 수학적 모델에서처럼 상태를 직접 비교하지 않고, 대신 score
함수를 사용하여 컨텍스트를 바이트 리스트로 투영합니다. 바이트 리스트는 먼저 길이에 따라, 그 다음 사전 순으로 정렬됩니다. 이는 소프트웨어 버전 관리에서 사용되는 것과 유사한 상당히 일반적인 구조로, 다양한 정렬을 표현하는 데에 매우 다재다능합니다.
프로토콜 모듈 내에 비교 함수를 정의하지 않는 이유는 무엇일까요? 첫째로, 그러한 함수가 전체 순서를 나타내야 한다는 요구사항을 강제하는 것이 어려울 것입니다. 점수 투영은 항상 이를 검증합니다(마지막 블록의 해시를 기반으로 동점을 처리할 수 있습니다). 둘째, 원칙적으로 우리는 서로 다른 프로토콜 간의 상태를 비교할 수 있는 능력이 필요합니다. 특정 프로토콜 수정 규칙은 이것이 실제로 발생할 가능성이 매우 낮게 만들 가능성이 큽니다만, 네트워크 쉘은 그 사실을 알지 못합니다.
parse_block_header
및 parse_operation
연산은 쉘에 노출되어 있으며, 이를 통해 프로토콜에 완전히 타입이 지정된 작업 및 블록을 전달할 수 있을 뿐만 아니라, 이러한 작업 및 블록이 잘 구성되어 있는지를 확인한 후 작업을 중계하거나 블록을 로컬 블록 트리 데이터베이스에 추가할지를 결정할 수 있습니다.
apply
함수는 프로토콜의 핵심입니다:
- 블록 헤더와 관련 작업 목록이 전달될 때, 이는 컨텍스트에 이루어진 변경 사항을 계산하고 수정된 사본을 반환합니다. 내부적으로, 버전 관리 시스템에서처럼, 차이점만이 블록의 해시를 버전 핸들로 사용하여 저장됩니다.
- 작업 목록만 전달될 경우, 가능한 많은 작업을 탐욕적으로 적용하려고 시도합니다. 이 함수는 프로토콜 자체에는 필요하지 않지만, 유효한 블록을 형성하려는 채굴자들에게 큰 유용성을 제공합니다.
protocol_hash
는 .ml 및 .mli 파일의 tarball의 sha256 해시입니다. 이 파일들은 즉석에서 컴파일됩니다. 그들은 작은 표준 라이브러리에 접근할 수 있지만, 샌드박스화되어 있으며 시스템 호출을 할 수 없습니다.
이 함수들은 프로토콜의 apply
함수를 통해 호출되며, 새로운 컨텍스트를 반환합니다.
프로토콜의 변경을 촉발할 수 있는 많은 조건들이 있습니다. 가장 단순한 버전에서, 이해 관계자의 투표는 프로토콜 변경을 촉발합니다. 더 복잡한 규칙은 점진적으로 투표에 부쳐질 수 있습니다. 예를 들어, 이해 관계자가 원한다면 그들은 새로운 수정안이 특정 속성을 존중한다는 것을 컴퓨터로 검증 가능한 증거를 제공해야 한다는 요구사항을 가진 수정안을 통과시킬 수 있습니다. 이는 사실상 “헌법적” 검증의 알고리즘적 검사입니다.
RPC
GUI 빌딩 작업을 더 쉽게 만들기 위해, 프로토콜은 JSON-RPC API를 노출합니다. API 자체는 다양한 절차의 유형을 나타내는 json 스키마로 설명됩니다. 일반적으로, get_balance
와 같은 함수들은 RPC에서 구현될 수 있습니다.
ocamlCopy codetype service = {
name : string list;
input : json_schema option;
output : json_schema option;
implementation : Context.t -> json -> json option Lwt.t
}
이름은 절차에서 네임스페이스를 허용하기 위해 문자열 리스트입니다. 입력과 출력은 선택적으로 json 스키마로 설명됩니다.
호출은 일반적으로 가장 점수가 높은 리프의 최근 조상인 주어진 컨텍스트에서 이루어집니다. 예를 들어, 가장 점수가 높은 리프보다 여섯 블록 위의 컨텍스트를 조회하면, 여섯 번의 확인을 거친 장부의 상태가 표시됩니다.
UI 자체는 특정 버전의 프로토콜에 맞춰 조정되거나, JSON 사양에서 일반적으로 파생될 수 있습니다.
초기 프로토콜
블록체인이 제네시스 해시에서 시작하는 것처럼, 테조스는 초기 프로토콜로 시작합니다. 이 프로토콜은 거의 모든 블록체인 기반 알고리즘을 반영하도록 수정될 수 있습니다.
경제
코인
초기에는 10,000,000,000 (십억) 코인이 있습니다(토큰 공급의 초기 범위는 크라우드세일 동안 발행된 토큰의 수가 될 것이며, 구체적으로 “십억”이 아닙니다. 이는 단지 자리 표시자였습니다. 이 규모 변경은 원칙에 아무런 영향을 미치지 않습니다), 소수점 두 자리까지 나눌 수 있습니다(정확성을 위해 실제로는 소수점 뒤에 여덟 자리를 사용할 수 있습니다). 우리는 단일 코인을 “tez(테즈)”로, 가장 작은 단위를 간단히 센트로 지칭할 것을 제안합니다. 또한, tez를 나타내기 위해 ꜩ (\ua729, “라틴 소문자 tz”) 기호를 사용할 것을 제안합니다. 따라서 1센트 = ꜩ0.01 = 한 테즈의 백분의 일입니다.
채굴 및 서명 보상
원칙
우리는 어떤 분산화된 화폐의 보안이 참여자들에게 금전적 보상을 제공함으로써 유도될 필요가 있다고 추측합니다(현재 보상 일정을 확정하는 과정에 있습니다). 포지션 페이퍼에서 설명한 바와 같이, 거래 비용만에 의존하는 것은 공유재의 비극을 겪습니다. 테조스에서, 우리는 보증금과 보상의 조합에 의존합니다.
보증금은 채굴자들(배서들도 보증금을 구매해야 함)이 구매하는 1년(보증금은 이제 단일 주기만 지속될 것입니다. 보증금 기간을 한 주기를 넘어 연장하는 것은 기회 비용이 높고 보안에 대한 이익이 적기 때문입니다) 보안 예치금입니다. 이중 서명이 발생한 경우, 이 보증금은 몰수됩니다.
1년(주기) 후, 채굴자들(및 배서들)은 그들의 기회 비용을 보상하기 위해 보증금과 함께 보상을 받습니다. 보안은 주로 보증금의 가치에 의해 제공되며, 보상은 그 가치의 작은 비율만이 필요합니다.
보증금의 목적은 필요한 보상의 양을 줄이고, 네트워크에 유리하게 손실 회피 효과를 사용할 수도 있습니다.
특징
초기 프로토콜에서, 블록을 채굴하면 ꜩ512의 보상을 제공하고 ꜩ1536의 보증금이 필요합니다. 블록에 서명하는 것은 32∆T^-1 테즈의 보상을 제공하는데, 여기서 ∆T는 서명된 블록과 그 전임자 사이의 시간 간격(분)입니다. 블록 당 최대 16개의 서명이 있고, 서명에는 보증금이 필요하지 않습니다. 이 숫자들은 100억 토큰 공급을 기반으로 하고 있으며, 우리는 이에 따라 조정할 것입니다. 시뮬레이션에서 확인된 바에 따르면, 블록 당 서명 수를 늘리면 포크의 난이도를 크게 높일 수 있으므로, 우리는 이를 늘릴 수도 있습니다.
따라서, 분당 한 블록의 채굴 속도를 가정할 때, 첫 해 후에 초기 화폐 질량의 약 8%가 안전 보증금의 형태로 보유되어야 합니다. 이는 위의 매개변수 조정에 따라 변경될 수 있습니다. 보상 일정은 최대 5.4%의 명목 인플레이션율을 시사합니다(총 블록 보상은 여전히 연 5%에서 시작하지만, 우리는 총 토큰 수에 점근적인 상한을 추가할 수 있습니다. 거버넌스 모델이 토큰 소유자의 이익과 일치할 때 이는 중요하지 않다고 생각하지만, 일부 사람들에게는 중요하므로 우리는 마지못해 이를 고려하고 있습니다). 명목 인플레이션은 중립적이며, 누구도 부유하게 하거나 빈곤하게 하지 않습니다.
1년이라는 기간은 블록의 타임스탬프에서 결정되며, 블록의 수에서 결정되지 않습니다. 이는 채굴자들이 한 해 동안 잠재적으로 변동성이 높은 자산을 보유하도록 하는 약속의 길이에 대한 불확실성을 제거하기 위한 것입니다.
기대
제안된 보상은 채굴자들에게 그들의 보증금에 대해 33%의 수익을 제공합니다(우리는 현재 이 매개변수를 재검토 중이지만, 곧 모든 당사자에게 의미 있는 방법을 확정할 예정입니다). 이 수익은 초기 단계에서 높아야 합니다. 채굴자와 서명자는 잠재적으로 변동성이 높은 자산을 전체 해 동안 보유하기로 약속하기 때문입니다(보증금은 전체 해가 아닌 한 주기 동안만 지속됩니다).
그러나, 테조스가 성숙함에 따라, 이 수익은 현재의 이자율로 점진적으로 낮아질 수 있습니다. 1% 미만의 명목 인플레이션율이 안전하게 달성될 수 있지만, 그렇게 하는 것이 실제로 의미가 있는지는 명확하지 않습니다.
분실된 코인
화폐 질량에 대한 불확실성을 줄이기 위해, 1년 동안 활동이 없는 주소들은(타임스탬프로 결정e됨) 그들이 포함하고 있는 코인과 함께 파괴됩니다. 백서에서 처음 제안된 대로, 비활성 주소는 1년 후에 자금을 잃지 않지만, 다시 활성화될 때까지 그들의 스테이킹 권리만을 잃게 됩니다. 즉, 주소가 비활성 상태인 경우, 블록 생성을 위해 선택되지 않으며(이는 합의 알고리즘을 느리게 할 것이며), 재활성화될 때까지 투표할 수 없습니다(참여율에 대한 불확실성을 피하기 위함입니다).
수정 규칙
수정안은 각각 N=217=131,072 블록 동안 지속되는 선거 주기를 통해 채택됩니다. 1분 블록 간격을 감안할 때, 이는 대략 세 달입니다. 선거 주기는 자체적으로 32,768 블록의 4분의 1로 나뉩니다. 이 주기는 초기 개선을 장려하기 위해 상대적으로 짧지만, 향후 수정안은 주기의 길이를 늘릴 것으로 예상됩니다(프로토콜 업그레이드 투표는 빠른 반복을 허용하기 위해 첫 해에 훨씬 더 빈번할 것입니다. 보안 조치로서, 테조스 재단은 투표 절차의 모든 결함을 배제할 때까지 12개월 후에 만료되는 거부권을 가질 것입니다). 채택은 특정 정족수를 충족해야 합니다. 이 정족수는 Q=80%에서 시작하지만, 평균 참여를 반영하여 동적으로 조정됩니다. 이는 적어도 분실된 코인을 다루기 위해 필요합니다.
첫 번째 분기
새 프로토콜을 나타내는 .ml 및 .mli 파일의 tarball의 해시를 제출함으로써 프로토콜 수정안이 제안됩니다. 이해관계자는 이러한 프로토콜 중 어느 것이든 승인할 수 있습니다. 이는 “승인 투표”로 알려진 특히 견고한 투표 절차입니다.
두 번째 분기
첫 분기에서 가장 많은 승인을 받은 수정안이 이제 투표 대상이 됩니다. 이해관계자는 찬성, 반대 투표를 할 수 있으며 명시적으로 기권할 수도 있습니다. 기권은 정족수에 포함됩니다.
세 번째 분기
정족수(명시적 기권 포함)가 충족되고, 수정안이 80%의 찬성을 받았다면, 수정안은 승인되어 테스트 프로토콜을 대체합니다. 그렇지 않다면 거부됩니다. 정족수가 q에 도달했다고 가정하면, 최소 정족수 Q는 다음과 같이 업데이트됩니다.
Q ← 0.8Q + 0.2q
이 업데이트의 목적은 분실된 코인으로 인해 시간이 지남에 따라 투표 절차가 멈추는 것을 방지하는 것입니다. 최소 정족수는 이전 선거에서 달성된 정족수의 지수 이동 평균입니다.
네 번째 분기
수정안이 승인되었다면, 세 번째 분기의 시작부터 테스트넷에서 실행되었을 것입니다. 이해관계자들은 테스트 프로토콜을 메인 프로토콜로 승격시키길 원하는지 두 번째로 투표합니다. 이것도 정족수가 충족되고 80%의 과반수가 필요합니다.
우리는 수정안에 대해 보수적인 접근 방식을 의도적으로 선택했습니다. 그러나, 이해관계자들은 이 정책을 완화하거나 강화하는 수정안을 채택할 자유가 있습니다.
지분 증명 메커니즘
개요
우리의 지분 증명 메커니즘은 Slasher[1], chain-of-activity[2], 그리고 proof-of-burn을 포함한 여러 아이디어의 혼합입니다. 다음은 알고리즘의 간략한 개요이며, 구성 요소는 아래에서 더 자세히 설명됩니다.
각 블록은 임의의 이해관계자(채굴자)에 의해 채굴되며, 임의의 이해관계자(서명자들)에 의해 제공된 이전 블록의 여러 서명을 포함합니다. 채굴과 서명은 모두 소액의 보상을 제공하지만, 이중 채굴이나 이중 서명의 경우에 몰수될 1년(처음 제안된 대로 1년이 아니라 한 주기 후에 언바운딩이 발생합니다. 주기보다 긴 기간을 연장하는 것은 많은 자본을 동결시키는 비용으로 정말로 보안을 개선하지 않았습니다)의 안전 예치금을 요구합니다.
프로토콜은 2048블록의 주기로 전개됩니다. 각 주기의 시작에서, 블록 채굴자들이 그 전전 주기에 선택하고 약속한 숫자들로부터 파생된 임의의 시드가 생성되며, 마지막에 공개됩니다. 이 임의의 시드를 사용하여, follow the coin 전략이 다음 주기에 대한 채굴 권한과 서명 권한을 특정 주소에 할당하는 데 사용됩니다. [그림 1]을 참조하세요.
시계
프로토콜은 블록 간에 최소 지연 시간을 요구합니다. 원칙적으로, 각 블록은 어떤 이해관계자에 의해서도 채굴될 수 있습니다. 그러나, 주어진 블록에 대해, 각 이해관계자는 임의의 최소 지연 시간을 준수해야 합니다. 가장 높은 우선 순위를 받는 이해관계자는 이전 블록 이후 1분 후에 블록을 채굴할 수 있습니다. 두 번째로 높은 우선 순위를 받는 이해관계자는 이전 블록 이후 2분 후에, 세 번째는 3분 후에, 그리고 이런 식으로 진행됩니다.
이는 이해관계자의 작은 부분만 기여하는 포크에서 블록 생성률이 낮을 것임을 보장합니다. 이것이 아니라면, 매우 높은 점수를 가진 것으로 주장되는 매우 긴 체인을 검증하도록 노드를 속임으로써 CPU 서비스 거부 공격이 가능할 것입니다.
임의의 시드 생성
채굴된 모든 블록은 채굴자가 선택한 임의의 숫자에 대한 해시 약속을 가지고 있습니다. 이 숫자들은 안전 보증금을 몰수당하는 벌칙 하에 다음 주기에 반드시 공개되어야 합니다. 이 엄격한 벌칙은 시드의 엔트로피를 공격하기 위해 숫자의 선택적 유보를 방지하기 위한 것입니다.
다음 주기의 악의적인 채굴자들은 이러한 공개를 검열하려고 시도할 수 있지만, 단일 블록에서 여러 숫자를 공개할 수 있기 때문에, 그들이 성공할 가능성은 매우 낮습니다.
주기 내에 공개된 모든 숫자들은 해시 목록에 결합되고, scrypt 키 파생 함수를 사용하여 루트로부터 시드가 파생됩니다. 키 파생은 전형적인 데스크탑 PC에서 블록의 평균 검증 시간의 일부 퍼센트에 해당하는 시간 동안 시드를 파생하는 것으로 조정되어야 합니다.
코인 따라가기 절차
임의의 이해관계자를 선택하기 위해, 우리는 코인 따라가기 절차를 사용합니다.
원칙
이 아이디어는 비트코인에서 follow-the-satoshi로 알려져 있습니다. 절차는 마치 모든 사토시가 고유한 일련번호를 가진 것처럼 “마치” 작동합니다. 사토시는 생성 시간으로 암시적으로 정렬되며, 임의의 사토시가 추첨되어 블록체인을 통해 추적됩니다. 물론, 개별 센트들은 직접 추적되지 않습니다. 대신, 입력이 결합되어 여러 출력을 통해 소비될 때 적용되는 규칙이 설명됩니다.
결국, 알고리즘은 각 키와 관련된 일련의 간격을 추적합니다. 각 간격은 사토시의 “범위”를 나타냅니다. 불행히도, 시간이 지남에 따라 데이터베이스는 점점 더 분열되어 클라이언트 측의 부피가 증가합니다.
코인 롤
우리는 10,000 테즈로 구성된 큰 “코인 롤”을 만들어 이전 알고리즘을 최적화합니다. 따라서 약 백만 개의 롤이 존재합니다. 데이터베이스는 각 롤을 현재 소유자에게 매핑합니다.
각 주소는 특정 롤 세트와 일부 잔돈을 보유합니다. 전체 롤의 일부를 소비하고자 할 때, 롤은 깨지고 그 일련번호는 롤의 일종인 “대기열”로 보내집니다. 모든 거래는 깨진 롤의 수를 최소화하는 방식으로 처리됩니다. 주소가 롤을 형성하기에 충분한 코인을 보유하고 있을 때마다, 일련번호가 대기열에서 뽑히고 롤이 다시 형성됩니다.
LIFO 우선순위는 비밀 포크 작업을 하는 공격자가 계좌 간에 잔돈을 섞어서 보유한 코인을 변경할 수 없도록 보장합니다.
이 접근 방식의 약간의 단점은 지분이 롤의 가장 가까운 정수 수로 내림됩니다. 그러나, 이는 follow-the-satoshi 접근 방식보다 효율성 측면에서 큰 개선을 제공합니다.
롤이 번호가 매겨지긴 하지만, 이 접근 방식은 Zerocash와 같은 기능성을 보존하는 프로토콜 사용을 배제하지 않습니다. 이러한 프로토콜은 같은 “대기열” 기술을 사용할 수 있습니다.
동기
이 절차는 단순히 잔액에 의해 가중치가 부여된 임의의 주소를 추첨하는 것과 기능적으로 다릅니다.
실제로, 비밀스러운 포크에서 채굴자는 임의의 시드 생성을 제어하고 적절한 주소를 미리 생성함으로써 자신에게 서명 및 채굴 권리를 할당하려고 시도할 수 있습니다. 롤이 임의로 선택된 경우 이를 달성하기가 훨씬 더 어렵습니다. 왜냐하면 비밀스러운 포크는 특정 롤의 소유권을 가짜로 만들 수 없으며, 따라서 시드에 적용된 해시 함수의 프리이미지를 시도하여 자신에게 서명 및 채굴 권리를 할당해야 하기 때문입니다.
실제로, 길이 N = 2048인 주기에서, 롤의 일정 부분 f를 보유한 사람은 평균적으로 fN만큼의 채굴 권리를 받게 됩니다. 그리고 실제로 받은 비율 f_0은 표준 편차가 아래와 같습니다.
공격자가 W 다른 시드를 통한 무차별 대입 검색을 수행할 수 있다면, 그의 예상 우위는 최대 아래만큼의 블록에 불과합니다.
예를 들어, 롤의 10%를 제어하는 공격자는 주기당 약 205블록을 채굴할 것으로 예상해야 합니다. 시드 제어를 시도하는 비밀 포크에서, 그가 1조 이상의 해시를 계산했다고 가정하면, 자신에게 약 302블록, 또는 약 14.7%의 블록을 할당할 수 있습니다.
주의할 점은:
- 시드에서 파생된 해시는 비용이 많이 드는 키 파생 함수이므로, 무차별 대입 검색을 비현실적으로 만듭니다.
- 블록을 채굴하는 선형적인 이득을 얻기 위해, 공격자는 기하급수적으로 증가하는 노력을 들여야 합니다.
블록 채굴
임의의 시드는 반복적으로 롤을 선택하는 데 사용됩니다. 첫 번째로 선택된 롤은 해당 이해관계자가 1분 후에 블록을 채굴할 수 있게 하고, 두 번째 롤은 2분 후에 — 그리고 이런 식으로 진행됩니다.
이해관계자가 시드를 관찰하고 다음 주기에 높은 우선순위 블록을 채굴할 수 있다는 것을 깨달았을 때, 그는 보안 예치금을 낼 수 있습니다.
특정 블록을 채굴하기 위해 어떤 이해관계자도 안전 예치금을 내지 않는 잠재적으로 문제가 될 수 있는 상황을 피하기 위해, 16분의 지연 후에는 예치금 없이 블록을 채굴할 수 있습니다.
보증금은 그들이 블록을 채굴하지 않는 모든 체인에서 즉시 구매자에게 암시적으로 반환됩니다.
블록 서명
현재 상태로도, 우리는 거의 작동하는 지분 증명 시스템을 가지고 있습니다. 우리는 체인의 가중치를 블록의 수로 정의할 수 있습니다. 그러나, 이것은 이기적 채굴의 한 형태로 문을 열 수 있습니다.
따라서 우리는 서명 체계를 도입합니다. 블록이 채굴되는 동안, 임의의 시드는 16개의 롤에게 16개의 서명 권리를 임의로 할당하는 데 사용됩니다.
서명 권리를 받은 이해관계자들은 채굴되고 있는 블록을 관찰한 다음 해당 블록의 서명을 제출합니다. 그런 다음 이 서명들은 부모의 블록체인 포함을 확보하려는 채굴자들에 의해 다음 블록에 포함됩니다.
서명자가 받는 서명 보상은 블록과 그 전임자 사이의 시간 간격에 반비례합니다.
따라서 서명자는 어느 시점에 생성된 최고의 블록이라고 진심으로 생각하는 것에 서명할 강력한 동기가 있습니다. 또한, 서명 보상은 블록이 블록체인에 포함될 때만 지급되므로, 어떤 블록에 서명할지에 대해 합의하는 데 강력한 동기가 있습니다.
최우선 순위 블록이 채굴되지 않는다면(아마도 채굴자가 온라인이 아닐 수 있음), 채굴자가 늦을 경우를 대비해 서명자가 잠시 기다리는 것이 유인될 수 있습니다. 그러나, 다른 서명자들은 최우선 순위 블록에 서명하기로 결정하고, 새 블록은 그 서명들을 포함하여 기다리는 사람들을 제외할 수 있습니다. 따라서, 채굴자들이 이 전략을 따르기 어려울 것입니다.
반대로, 서명자들이 다른 서명자들이 그렇게 할 것이라는 두려움으로 본인들이 보는 첫 번째 블록에 서명하기 시작하는 균형을 상상할 수 있습니다. 그러나 이는 아무도 이득을 보지 않는 매우 인위적인 상황입니다. 서명자들이 이러한 균형이 가능하다고 생각할 동기가 없으며, 그들의 프로그램 코드를 이런 방식으로 행동하도록 수정할 이유도 없습니다. 운영을 방해하려는 악의적인 이해관계자는 다른 사람들이 따라오지 않을 것이므로, 이 전략을 따르려고 시도함으로써 스스로에게만 해를 끼칠 것입니다.
체인의 가중치
가중치는 서명의 수입니다.
고발
블록의 이중 채굴이나 이중 서명을 방지하기 위해, 채굴자는 자신의 블록에 고발을 포함할 수 있습니다.
이 고발은 두 개의 서명 형태를 취합니다. 각 채굴 서명 또는 블록 서명은 블록의 높이에 서명하여, 부정 행위의 증거를 매우 간결하게 만듭니다.
우리는 누구나 부정 행위를 고발할 수 있도록 허용할 수 있지만, 정말로 블록 채굴자를 넘어서 다른 사람에게 허용할 이유가 없습니다. 실제로, 채굴자는 부정 행위의 증거를 단순히 복사하여 자신의 발견으로 주장할 수 있습니다.
한 번 당사자가 이중 채굴이나 이중 서명으로 유죄 판정을 받으면, 안전 보증금은 몰수됩니다.
스마트 계약
계약 유형
미사용 출력 대신, 테조스는 상태를 가진 계정을 사용합니다. 이 계정들이 실행 가능한 코드를 지정할 때, 그것들은 일반적으로 계약이라고 알려져 있습니다. 계정이 계약의 한 유형(실행 가능한 코드가 없는)이기 때문에, 우리는 전반적으로 둘 다 “계약”으로 지칭합니다.
각 계약에는 “관리자”가 있으며, 계정의 경우 단순히 소유자입니다. 계약이 사용 가능으로 표시된 경우, 관리자는 계약과 관련된 자금을 사용할 수 있습니다. 또한, 각 계약은 지분 증명 프로토콜에서 블록을 서명하거나 채굴하는 데 사용되는 공개 키의 해시를 지정할 수 있습니다. 개인 키는 관리자가 제어할 수도 있고 그렇지 않을 수도 있습니다.
공식적으로, 계약은 다음과 같이 표현됩니다:
ocamlCopy codetype contract = {
counter: int; (* 반복 공격을 방지하기 위한 카운터 *)
manager: id; (* 계약의 관리자 공개 키의 해시 *)
balance: Int64.t; (* 보유한 잔액 *)
signer: id option; (* 서명자의 id *)
code: opcode list; (* 오피코드 목록으로 구성된 계약 코드 *)
storage: data list; (* 계약의 저장소 *)
spendable: bool; (* 관리자에 의해 자금이 사용될 수 있는가? *)
delegatable: bool; (* 관리자가 서명 키를 변경할 수 있는가? *)
}
계약의 핸들은 그 초기 내용의 해시입니다. 기존 계약과 충돌할 해시를 가진 계약을 생성하려는 시도는 유효하지 않은 작업이며 유효한 블록에 포함될 수 없습니다.
데이터는 연합 유형으로 표현됩니다.
ocamlCopy codetype data =
| STRING of string
| INT of int
여기서 INT는 64비트 정수이며, 문자열은 최대 1024바이트의 배열입니다. 저장소 용량은 정수를 8바이트로, 문자열을 그 길이로 계산하여 16,384바이트로 제한됩니다.
생성
생성 작업은 새 계약을 만드는 데 사용될 수 있으며, 계약의 코드와 계약의 저장소의 초기 내용을 지정합니다. 핸들이 이미 존재하는 계약의 핸들인 경우, 생성은 거부됩니다(실수나 악의에 의한 것이 아닌 한 발생할 이유가 없습니다).
계약은 활성 상태를 유지하기 위해 최소 1의 잔액이 필요합니다. 잔액이 이 수치 아래로 떨어지면, 계약은 파괴됩니다.
거래
거래는 한 계약에서 다른 계약으로 보내진 메시지이며, 이 메시지는 다음과 같이 표현됩니다:
ocamlCopy codetype transaction = {
amount: amount; (* 보내지는 금액 *)
parameters: data list; (* 스크립트에 전달된 매개변수 *)
(* 반복 공격을 피하기 위한 카운터 (송장 id) *)
counter: int;
destination: contract hash;
}
이러한 거래는 관리자의 키를 사용하여 서명되었을 때 계약에서 보낼 수 있으며, 계약에서 실행 중인 코드에 의해 프로그래밍 방식으로 보낼 수도 있습니다. 거래가 수신되면, 금액은 목적지 계약의 잔액에 추가되고 목적지 계약의 코드가 실행됩니다. 이 코드는 전달된 매개변수를 사용할 수 있으며, 계약의 저장소를 읽고 쓸 수 있고, 서명 키를 변경하고, 다른 계약에 거래를 게시할 수 있습니다.
카운터의 역할은 재생 공격을 방지하는 것입니다. 계약의 카운터가 거래의 카운터와 같을 때만 거래가 유효합니다. 거래가 적용되면 카운터가 하나 증가하여 거래가 재사용되는 것을 방지합니다.
거래에는 클라이언트가 유효하다고 판단하는 최근 블록의 블록 해시도 포함됩니다. 공격자가 포크로 긴 재조직을 강제하는 데 성공한다면, 그는 이러한 거래를 포함할 수 없게 되어 포크가 분명히 가짜임을 알 수 있습니다. 이는 마지막 방어선으로, TAPOS는 긴 재조직을 방지하는 데는 훌륭한 시스템이지만, 단기 이중 지출을 방지하는 데는 그다지 좋은 시스템이 아닙니다.
(계정 핸들, 카운터) 쌍은 비트코인에서의 미사용 출력과 대략적으로 동등합니다.
저장소 비용
저장소는 네트워크에 비용을 부과하기 때문에, 저장소가 증가하는 각 바이트에 대해 최소 ꜩ 1의 수수료가 부과됩니다. 예를 들어, 거래 실행 후 정수가 저장소에 추가되고 기존 문자열에 문자열이 10자 추가되었다면, 계약의 잔액에서 ꜩ 18이 인출되어 소멸됩니다.
코드
언어는 스택 기반으로, 고수준 데이터 타입과 프리미티브 그리고 엄격한 정적 타입 검사를 가집니다. 그 설계는 Forth, Scheme, ML, 그리고 Cat에 영감을 받았습니다. 지시어 세트의 전체 사양은 [5]에 있습니다. 이 사양은 언어의 전체 지시어 세트, 타입 시스템, 그리고 의미론을 제공합니다. 이것은 정확한 참조 매뉴얼로 의도되었으며, 쉬운 소개서가 아닙니다.
수수료
지금까지, 이 시스템은 이더리움이 거래를 처리하는 방식과 유사합니다. 그러나, 우리는 수수료를 처리하는 방식에서 차이가 있습니다. 이더리움은 프로그램의 실행 시간과 선형적으로 증가하는 수수료를 요구함으로써 임의로 긴 프로그램의 실행을 허용합니다. 불행히도, 이는 한 채굴자에게 거래를 검증할 유인을 제공하지만, 이 거래를 또한 검증해야 하는 다른 채굴자들에게는 그러한 유인을 제공하지 않습니다. 실제로, 스마트 계약에 사용될 수 있는 대부분의 흥미로운 프로그램은 매우 짧습니다. 따라서, 우리는 프로그램이 실행될 수 있는 단계 수에 대한 하드 캡을 부과함으로써 구조를 단순화합니다.
하드 캡이 일부 프로그램에 대해 너무 엄격하다면, 실행을 여러 단계로 나누고 여러 거래를 사용하여 전체를 실행할 수 있습니다. 테조스가 수정 가능하기 때문에, 이 캡은 미래에 변경될 수 있으며, 새로운 오피코드로서 고급 프리미티브를 도입할 수 있습니다.
계정이 허용한다면, 서명 키는 변경 요청을 하는 서명된 메시지를 발행함으로써 변경될 수 있습니다.
결론
우리는 매력적인 초기 프로토콜을 구축했다고 느낍니다. 그러나, 테조스의 진정한 잠재력은 이해관계자들이 그들에게 가장 잘 봉사한다고 느끼는 프로토콜을 결정하는 데 권한을 부여함에 있습니다.
참고문헌
- [1] Vitalik Buterin. Slasher: A punitive proof-of-stake algorithm. https://blog.ethereum.org/2014/01/15/slasher-a-punitive-proof-of-stake-algorithm/, 2014.
- [2] Ariel Gabizon Iddo Bentov and Alex Mizrahi. Cryptocurrencies without proof of work. http://www.cs.technion.ac.il/~idddo/CoA.pdf, 2014.
- [3] Peter Suber. Nomic: A game of self-amendment. http://legacy.earlham.edu/~peters/writing/nomic.htm, 1982.
- [4] Jérôme Vouillon. Lwt: a cooperative thread library. 2008.
- [5] Tezos project. Formal specification of the tezos smart contract language. https://tezos.com/pages/tech.html, 2014.