스마트 컨트랙트는 블록체인 네트워크에서 호스팅되고 실행되는 프로그램이다. 조건을 지정해두고, 해당 조건이 충족되면 미리 정해둔 계약을 이행하는 방식으로 코드를 작성한다.
스마트 컨트랙트는 일반적인 중앙 집중식 서버가 아니라 탈중앙화된 블록체인에서 실행하기 때문에, 계약 조건이나 이행 내용을 변조하기 어려우며 계약을 신뢰하기 위한 제삼자가 필요하지 않다.
스마트 컨트랙트의 로직은 다음과 같다.
if or when (이벤트 x가 실행되면) { // 조건
액션 y를 실행하라 // 계약 이행
}
코드가 블록체인 네트워크에 올라가면, 네트워크는 미리 결정된 조건이 충족되고, 검증된 경우 조건에 따른 액션을 실행한다. 코드에 따라 계약이 실행됨을 보장하는 "결정론적"인 상태인 다자간 디지털 계약이 바로 스마트 컨트랙트이다.
전에 포스팅에서 UTXO는 "미사용 트랜잭션 출력값"이라는 것을 배웠다. 비트코인은 특정 지갑에 저장되는 것이 아니라, UTXO에 저장되며, 이 UTXO를 통해 코인의 존재 여부를 확인할 수 있다고 했다. 그렇다면 UTXO가 정확히 어떻게 동작하는지 확인해보자.
UTXO를 이해하기 위해서는 우선 트랜잭션 구조를 알아야 한다.
하나의 트랜잭션은 위와 같은 모양이며, 다양한 데이터 구조들로 구성되어 있다.
앨리스가 밥에게 6비트를 보낸다고 가정해보자.
앨리스는 10BTC가 들어있는 하나의 UTXO를 가지고 있다.
트랜잭션은 입력을 통해 우선 10BTC가 든 출력을 해제한다. 그리고 두 가지 새로운 출력을 만만든다. 하나는 밥에게 지불되어야 할 6BTC가 든 출력이고, 다른 하나는 앨리스에게 거슬러줄 4BTC가 든 출력이다. 이 새로운 출력들은 아직 다른 입력에 의해 해제되지 않았기 때문에 UTXO(미사용 트랜잭션의 출력값)가 된다.
UTXO 모델은 확장성이 좋다는 장점이 있다. 트랜잭션 로직이 매우 단순하기 때문에 병렬적으로 트랜잭션을 검증할 수 있다.
비트코인에서는 스크립트(Script)라는 스크립터 언어를 사용해 스마트 컨트랙트를 구현한다. 이를 비트코인 스크립트라고 부른다. 비트코인 스크립트는 일반적인 프로그래밍 언어와는 다르게 어떤 공식적인 문법이나 구문이 있는 것이 아니라 간단한 연산 목록으로 구성되어 있다. 스크립트에서 사용하는 연산들은 opcode에 해당하며, 이 연산들은 C++로 작성되어 있다.
스크립트는 트랜잭션에 연결되어 있다. 네트워크의 모든 노드는 트랜잭션을 받을 때마다 자신의 로컬 컴퓨터에서 트랜잭션에 연결된 스크립트를 실행한다. 이 스크립트 실행을 통해 비트코인의 송금이 이루어진다.
트랜잭션 구조를 복습해보면, 비트코인 트랜잭션은 입력과 출력 목록을 가지고 있다. 출력은 잠금(lock)과 값을 가지고 있으며, 입력은 연결된 과거의 출력을 가리키는 포인터와 해제 키를 가지고 있다. 스크립트는 이 잠금과 해제 메커니즘에 관한 것이다.
입력과 출력의 구조는 다음과 같다.
노드는 피어로부터 트랜잭션을 받으면 먼저 해당 트랜잭션 안에 들어있는 입력과 출력 목록에서 각 입력과 출력에 해당하는 ScriptSig와 ScriptPubkeys를 추출한다. 그리고 기존 블록들을 찾아보며 입력과 연결된 이전 출력을 찾고, 각 입력과 출력에 들어있는 ScriptSig와 ScriptPubekey를 연결시킨다.
ScriptSig와 ScriptPubkey는 각각 연속적인 정보를 담고 있다. 노드가 ScriptSig와 ScriptPubkey를 연결하고, 그 연결한 전체 시퀀스를 파싱하면 온전한 스크립트 코드가 나타나게 된다. 노드는 이 코드를 단계별로 실행한다.
스크립트는 역폴란드 표기법(reverse polish notation)으로 작성된 스택 기반 튜링 불완전 언어이다. 다 이해할 필요는 없다. 여기서 중요한 것은 스크립트는 스택 구조로 작동한다는 것이다.
스택구조는 길쭉한 프링글스 감자칩을 생각하면 된다. 처음에 감자칩을 순서대로 넣으면 가장 나중에 넣은 감자칩부터 꺼내어 먹는다. 이것이 스택이며, 스택을 쉽게 FILO(First In Last Out)라고 부르기도 한다.
스크립트는 빈 스택에서 시작하며, 이 스택에서 데이터가 들어오고 나간다.
스크립트 프로그램은 두 종류의 객체를 가지고 있다.
opcode란?
opcode는 로우 레벨 기계 언어로, 약 140 종류의 연산이 있음.
스크립트는 이 opcode와 DATA를 일렬로 늘어놓은 것이다. 여기서 포인터(pointer)는 일렬로 늘어진 opcode와 데이터를 순서대로 하나씩 가르킨다. 만약 포인터가 데이터를 가리키면 데이터를 스택에 넣고, opcode를 가리키면 스택에서 데이터를 꺼내온다. opcode는 스택에서 데이터를 하나 이상 가져올 수 있지만, 중요한 것은 스택 구조이기 때문에 가장 나중에 들어온 데이터부터 가져온다는 것이다.
스크립트 실행이 성공적이면, 스택의 가장 상단에 있는 요소는 1이 된다. 만약 스크립트를 끝까지 실행했는데도 스택 최상단에 1이 아닌 다른 값이 들어있다면 스크립트 실행을 실패한 것으로 간주한다.
노드가 네트워크로부터 새로운 트랜잭션을 받으면, ScriptSig와 ScriptPubkey 필드를 추출하여 연결하여 최종적으로 <ScriptSig><ScriptPubkey>
형태의 스크립트를 얻게 된다. 노드는 이 스크립트와 빈 스택 하나를 사용해 스크립트를 실행하고, 실행이 완료되면 최상위 스택 요소가 1인지 확인한다. 1이면 트랜잭션이 유효하다가 간주하고 노드는 트랜잭션을 주변 노드들에게 전파한다. 만약 트랜잭션이 유효하지 않은 경우 트랜잭션을 보내지 않고, 네트워크에 트랜잭션이 공유되지 않게 된다. 이와같이 트랜잭션은 전체 네트워크에 상태를 지키는 문지기 같은 역할을 하게 된다.
두 개의 데이터와 하나의 opcode만 사용하는 가장 간단한 종류의 비트코인 스크립트이다. 비트코인 초기 버전에는 이러한 유형의 스크립트가 사용되었다. P2PK는 퍼블릭 키에 직접 코인을 송금하는 데 사용합니다. 수신자의 공개 키를 직접 노출하기 때문에 보안에 취약하며, 오늘날에는 사용되지 않는다.
ScriptPubkey는 잠금을 정의하는 데이터 구조이며, ScriptSig는 해제 키를 정의하는 데이터 구조이다.
ScriptSig는 트랜잭션 입력(Input) 내부에 존재하며, ScriptPubkey는 이 트랜잭션 입력이 잠금을 해제하려는 이전 트랜잭션의 출력(Output)에서 추출된다.
“잠금을 해제한다"는 것은 ScriptSig와 ScriptPubkey를 연결하여 파싱해 스크립트 코드로 만들어 실행하고, 실행 후 스택 최상단에 값이 1인 요소를 남기는 프로세스이다.
P2PK 유형의 트랜잭션에서의 ScriptPubkey와 ScriptSig 필드는 위의 그림과 같다. 전체 P2PK 스크립트는 서명(signature), 공개 키(public key), OP_CHECKSIG라는 opcode로 구성된다. 이 OP_CHECKSIG는 서명이 주어진 공개 키와 연결되는지 확인한다. 만약 연결되면 스택에 1을 반환하고, 그렇지 않으면 0을 반환한다.
자세한 순서는 다음과 같다.
빈 스택에서 포인터가 서명을 가르킨다.
서명은 데이터이므로 스택에 들어갑니다. 포인터는 그 다음 요소인 공개 키를 가르킨다.
공개 키도 데이터이니 스택에 넣는다. 포인터는 이제 OP_CHECKSIG를 가르킨다.
OP_CHECKSIG는 스택에서 아이템 두개(공개키와 서명)를 꺼내는 opcode이다. OP_CHECKSIG는 스택에서 공개키와 서명을 꺼내고, ECDSA 알고리즘을 사용해 서명을 검증한다. 만약 검증에 성공하면 1, 실패하면 0을 스택에 넣는다.
ECDSA 알고리즘 (ECDSA; Elliptic Curve Digital Signature Algorithm)은 타원곡선 알고리즘에 디지털 서명을 추가한 알고리즘이다. 비트코인과 이더리움에서 서명을 검증할 때 사용한다.
검증을 마치고 스택에 1이 들어있으면, 트랜잭션 검증이 완료되고 UTXO가 해제된다.
스크립트가 성공적으로 실행되면 트랜잭션은 유효한 것으로 간주된다. 이전 UTXO가 소비되고, 해당 트랜잭션의 출력 목록에 따라 새로운 UTXO가 생성된다.
P2PKH는 P2PK 메커니즘과 거의 동일하며, 한 가지 차이점은 P2PKH에서는 ScriptPubkey가 공개 키의 해시값을 가지고 있다는 점이다. 따라서 UTXO는 수신자의 공개 키를 공개 할 필요가 없다.
P2PK와는 다르게, 서명과 공개키가 ScriptSig에 들어있다. ScriptPubkey는 P2PK에서와 다르게 여러개의 opcode를 가지고 있으며, 수신자의 공개키를 해싱한 값인 Hash 1 객체가 들어있다. 비트코인을 보내는 동안 송신자는 공개키의 해시값을 보내기 때문에 스크립트 이름의 PayToPubkeyHash인 것이다.
자세한 순서는 다음과 같다.
빈 스택에서, 포인터는 서명을 가르킨다.
서명은 데이터이기 때문에 스택에 들어가고, 포인터는 다음 요소인 공개 키를 가르킨다.
공개 키도 데이터이기 때문에 스택에 들어가고, 포인터는 다음 요소인 OP_DUP을 가르킨다. OP_DUP은 스택 최상단 요소를 복사하는 opcode이다. OP_DUP이 실행되면 스택에는 공개 키가 두개 쌓이게 된다. 포인터는 다음 요소인 OP_HASH160을 가르킨다.
OP_HASH160은 스택 최상단 요소를 해싱하는 opcode이다. 최상단에 있는 공개 키가 해싱되어 Hash 2가 된다. 포인터는 다음 요소인 Hash 1을 가르킨다.
Hash 1은 데이터이기 때문에 스택에 쌓인다. 포인터는 다음 요소인 OP_EQUALVERIFY를 가르킨다.
OP_EQUALVERIFY는 스택에 있는 두 요소가 같은지 확인하는 op_code이다. 만약 두 요소가 같다면, 해당 요소 두 개를 제거하고, 다르다면 실행에 실패하게 된다.
만약 ScriptSig에 올바른 공개 키가 들어있었다면 ScriptPubkey에 들어있던 Hash 1과 공개키를 해싱한 값인 Hash 2가 동일할 것이고, 스택에서 제거된다.
이제 포인터는 다음 요소인 OP_CHECKSIG를 가리킨다.
OP_CHECKSIG는 앞서 P2PK와 동일하게 동작한다. 공개 키와 서명을 꺼내, ECDSA 알고리즘으로 서명을 검증하고, 서명이 올바르다면 스택에 1을 쌓는다.
실행이 완료되고 1이 남아있다면 검증이 성공한 것이고, UTXO가 해제된다.
P2PKH는 서명에 대해 공개 키로 검증을 한다는 점에서는 P2PK 스크립트와 동일하지만, 공개 키가 아닌 공개 키 해시값을 사용한다는 점에서 우회적인 방식으로 좀 더 좋은 프라이버시를 제공한다.
비트코인 스크립트가 스마트 컨트랙트인 이유
UTXO의 ScriptPubkey는 일종의 수학적 퍼즐의 형태이고, 올바른 ScriptSig를 추가함으로써 이 퍼즐을 풀 수 있다
UTXO는 일종의 "계약"으로 정의될 수 있으며, 이 계약은 유효한 해제 조건이 주어지면 출력에 잠긴 비트코인을 이동시킨다. 이 계약의 실행은 비트코인 네트워크가 보장하기 때문에 계약 이행을 강제하기 위한 중개자가 필요하지 않는다.
비트코인은 최초의 블록체인 알고리즘이지만 확장성이나 성능 등의 한계가 존재했다. 오늘날에는 비트코인의 단점을 극복하기 위한 다양한 플랫폼과 프로젝트가 존재한다.
비트코인의 TPS는 트랜잭션 양과 관계없이 고정되어 있기 때문에 저장해야 할 트랜잭션이 많아질 수록 트랜잭션이 처리되는 속도가 느려지게 된다.
라이트닝 네트워크는 이러한 성능 문제를 해결한다. 트랜잭션 내용은 블록체인 외부에 저장하고, 트랜잭션의 최종 결과만 블록체인에 올린다. 트랜잭션이 블록체인 바깥에서 이루어지기 대문에 트랜잭션이 처리되기까지 대기 시간이 필요하지 않아 즉시 처리된다.
이러한 기능을 구현하기 위한 핵심 기술은 다중 서명(multi signature, multi-sig)과 시간 잠금 계약(hashed timelocke contract)이다. 다중 서명을 통해, 블록체인 외부에서 트랜잭션을 실행할 때는 계약 당사자들끼리 합의를 진행하고, 트랜잭션 결과를 체인에 기록할 때 계약 당사자들의 개인 키로 공동 계좌를 만들어 비밀 키를 생성한다. 또한 시간 잠금 계약은 일정 시간이 지나 트랜잭션이 확정되었을 때까지 트랜잭션을 변경할 수 없게 하여 계약 당사자가 트랜잭션을 임의로 수정하지 못하도록 한다.
루트스탁은 비트코인에 스마트 컨트랙트 기능을 탑재하는 사이드체인 프로젝트이다. 비트코인에서도 스마트 컨트랙트 구현이 가능하지만, 기본적으로 연산에 대한 수수료가 비싸기 때문에 실질적으로는 사용이 어려웠다. 또한 비트코인 스크립트는 튜링 불완전하기 때문에 사용성 측면에서 제약이 컸다.
루트스탁은 2 way peg를 이용해 비트코인에 튜링 완전한 스마트 컨트랙트를 지원하는 블록체인을 쌍방향으로 연결하며, 병합 채굴을 통해 비트코인 채굴 노드가 사이드체인 블록까지 채굴할 수 있도록 연결한다. 이를 통해 비트코인 네트워크에서도 튜링 완전한 스마트 컨트랙트를 실행할 수 있게 되었다.
탭루트는 2021년 11월 중으로 이루어질 비트코인 업그레이드를 의미한다. 탭루트에는 슈노르 서명과 MAST 등 중요한 사항들이 포함되어 있다.
슈노르 서명 방식(Schnorr Signature)은 기존의 서명 방식인 다중 서명 방식의 단점을 보완하기 위해 도입되었다. 기존 다중 서명은 트랜잭션의 크기가 커지기 때문에 외부에서 추적하기 더 쉬워지고, 이로 인해 프라이버시에 취약해진다는 단점이 있다. 또한 트랜잭션의 크기 자체가 커지기 때문에 트랜잭션 처리 속도에도 영향을 준다. 슈노르 서명은 기존 다중 서명처럼 여러 개의 서명을 받는 것이 아니라, 여러 개의 서명을 기반으로 한 하나의 공동 서명을 만들기 때문에 트랜잭션의 크기가 커지지 않는다
MAST(Merkelized Abstract Syntax Tree, 머클 추상화 구문트리)는 비트코인 스크립트에서 해시 값을 추출하는 자료구조로, 스크립트의 조건 중 필요한 부분만 먼저 검증할 수 있도록 하였다. 또한 MAST를 슈노르 서명과 결합시키는 경우, 서명을 한번만 해도 트랜잭션이 이루어질 수 있기 때문에 속도가 개선되고, 효율성이 증대되는 효과가 있다.
MAST와 슈노르 서명을 통해 스크립트 실행에 대한 효율성이 극도로 커지기 때문에, 궁극적으로 기존에 제약이 많았던 비트코인 스마트 컨트랙트의 활용성이 확장될 것으로 기대된다.
이더리움은 개발자들이 분산형 어플리케이션 dApp을 만들 수 있도록 튜링 완전한 언어인 솔리디티(Solidity)를 제공하였으며, 이더리움 네트워크에 올라간 솔리디티 코드는 EVM(Ethereum Virtual Machine)을 통해 실행된다.
비트코인과 이더리움의 차이점
VM은 우리가 짠 코드와 우리 컴퓨터(하드웨어) 사이에서 추상화 수준을 생성한다. 만약, 우리가 작성한 코드가 맥북과 LG 그램에서 각기 다르게 동작하면 우리는 각 하드웨어에 맞게 코드를 수정해야 할 것이다. 또 내가 짠 프로그램 A와 프로그램 B가 동시에 실행되다가 서로 충돌될 수도 있다.
그래서 보통은 하드웨어 위에 가짜 컴퓨터를 하나 만들어둔다. 우리가 맥북을 쓰든 LG 그램을 쓰든 동일하게 동작할 수 있도록 가짜로 컴퓨터를 만들어 실행한다. 이 가짜 컴퓨터는 어느 컴퓨터에서도 동일하게 동작한다. 실제 실행은 하드웨어에서 일어나는 것이지만, 중간에 있는 가짜 컴퓨터 때문에 어떤 하드웨어이든 동일하게 동작하는 것이다. 이 가짜 컴퓨터는 실제로는 존재하지 않기 때문에 가상 머신(Virtual Machine)이라고 한다.
우리가 짠 코드와 이더리움 블록체인 사이에 있는 가상 머신으로, 블록체인에서 코드가 실행될 수 있도록 한다.
이더리움 스마트 컨트랙트에 기반한 dApp은 솔리디티라는 언어로 작성된다. 우리는 솔리디티로 작성된 dApp을 이더리움 네트워크에 올려야 한다.
그런데, 솔리디티 언어는 고급 언어이기 때문에 EVM이 이해하기 어렵다. EVM은 솔리디티를 읽을 수 없기 때문에, 먼저 우리가 작성한 솔리디티 코드를 solc를 이용해 컴파일하여 EVM이 읽을 수 있는 바이트코드 형태로 만든다. 그리고 이 바이트코드를 geth를 이용해 이더리움 네트워크에 올린다.
블록체인에 저장된 바이트코드 형태의 dApp은 EVM에서 실행하게 되며, EVM에서는 바이트코드를 opcode로 변환하여 실행한다. 솔리디티로 작성된 스마트 컨트랙트는 EVM에서 동작하기 때문에 특정 운영체제나 하드웨어에 종속되지 않는다.
💡 즉, 솔리디티 코드 → 바이트코드 → opcode 흐름이다.
opcode를 효율적으로 저장하기 위해, opcode는 바이트코드(bytecode)로 인코딩된다. 모든 opcode에는 1바이트가 할당된다. 가령 STOP 은 0x00이다.
가령 0x6001600101이라는 바이트코드가 있다고 하자.
코드가 실행되면 바이트코드는 바이트로 쪼개집니다. (1 바이트는 2개의 16진수 문자이다.) 0x60~0x7f(opcode PUSH1 ~ PUSH32) 사이의 바이트는 데이터를 스택에 넣기 때문에 다른 opcode와는 다르게 처리된다.
첫번째 명령어의 첫번째 바이트 0x60는 PUSH1 연산입니다. PUSH1 연산 뒤에는 스택에 추가할 데이터가 온다. 따라서 명령어의 두번째 바이트 0x01을 스택에 추가한다
두번째 명령어는 첫번째 명령어와 같기 때문에 동일하게 스택에 0x01이 쌓인다.
세번째 명령어 0x01은 ADD 연산이다. 이 연산은 스택에서 요소 두 개를 꺼내오고 두 요소의 합을 스택에 추가한다. 스택에서 0x01, 0x01 두 개를 꺼내오고 더한 결과값 0x02를 스택에 추가한다.
솔리디티는 스마트 컨트랙트를 실행하는 객체 지향(object-oriented), 정적 타입(static typed), 고급(high-level) 스크립트 언어로, EVM에서 실행된다.
솔리디티는 C++과 파이썬, 자바스크립트를 기반으로 만들어졌으며, 이더리움 네트워크에서 스마트 컨트랙트를 생성할 수 있도록 설계됐다. 솔리디티는 정적 타입 스크립트 언어로, 런타임 언어와는 달리, 컴파일 시 제약 조건을 확인하고 적용한다.
만약 개발자가 은행 앱을 만들어 앱스토어에 올리려면, Swift라는 언어를 사용해 프로그래밍 해야 한다. 그리고 swift로 만든 앱을 앱스토어에 올리기 위해 애플에 신청하면, 애플은 앱을 심의하고 적합한 경우 앱스토어에 올린다. 그리고 사용자는 언제든지 앱스토어에서 원하는 앱을 다운로드 받을 수 있다.
이더리움도 마찬가지이다. 이더리움 네트워크에서 언제든지 원하는 분산형 앱(dApp)을 사용할 수 있다. 이더리움 네트워크에 dApp을 올리기 위해서는 솔리디티로 프로그래밍을 해야 한다. 다만 앱스토어처럼 중앙기관에 심의를 받을 필요 없이 누구나 약간의 비용만 내면 이더리움 네트워크에 자신이 만든 dApp을 올릴 수 있다.
비트코인 스크립트와 비교해서 솔리디티의 가장 큰 특징은 튜링 완전/불완전이다.
dApp의 백엔드 개발 도구들은 대표적으로 다음과 같은 것들이 있다.
본격적으로 EVM의 내부 동작 구조를 확인해보자.
이더리움은 거래에 기반을 둔 상태 머신(transaction-based state machine)이다.
상태 머신이란 일련의 입력을 읽고, 그 입력을 기반으로 새로운 상태로 전환하는 것을 의미한다.
이더리움이라는 상태 머신은 Genesis state에서 시작한다. 그리고 트랜잭션이 실행되면, 다음 상태로 전환하게 된다. 더이상 트랜잭션이 들어오지 않은 마지막 상태가 바로 이더리움의 현재 상태이다.
이더리움의 상태에는 수천 개의 트랜잭션이 있다. 이 트랜잭션은 "블록"이라는 그룹에 묶여 있으며, 블록은 직전에 만들어진 블록과 이어져 있다.
이더리움에는 EOA(External Owned Account)와 CA(Contract Account)라는 두 종류의 계정이 있다. 모든 계정은 주소로 식별되며, 동일한 주소 공간을 가진다. EVM은 이 160 bit 길이의 주소들을 처리한다.
EOA와 CA 모두 잔액(balance), 논스(nonce), 스토리지(stroage), 컨트랙트 코드(contract code)로 구성되어 있따.
EOA의 주소는 상응하는 비밀 키를 가지고 있지만, CA는 비밀키가 없다.
또한 CA는 스스로 새로운 트랜잭션을 만들 수 없다. CA는 EOA나 다른 CA에게서 받은 트랜잭션에 대한 응답에 대해서만 트랜잭션을 만들 수 있다.
따라서 이더리움 블록체인에서 일어나는 모든 액션은 항상 EOA에서 만든 트랜잭션에서부터 시작한다
이더리움의 전역 상태는 계정 주소와 계정 상태를 매핑한 것으로 구성되어 있다. 이 매핑은 머클 패트리샤 트리(Merkle Patricia Tree) 형태로 저장되어 있다.
머클 트리(Merkle tree)는 이진 트리의 한 종류로, 다음의 특징을 가지고 있다.
트리 맨 아래에 있는 데이터는 저장하려는 데이터를 청크로 분할한 다음, 각 청크를 두개씩 모아 해시를 취하여 부모 노드를 만든다. 그렇게 하나의 루트 노드가 만들어질때까지 동일한 과정을 반복한다.
이더리움의 블록 헤더에는 세 개의 머클 트리 구조의 루트 노드의 해시값이 저장되어 있다.
부모 노드는 자식 노드의 해싱값이기 때문에, 단방향 함수인 해시의 특성상 자식 노드의 값이 조금이라도 바뀌면 모든 부모 노드의 값도 바뀌게 된다. 따라서 머클 패트리샤 트리에서 변조가 시도되면 그 즉시 발견된다. 따라서 루트 노드는 데이터에 대한 보안 ID처럼 사용될 수 있다. 또한 블록 헤더는 상태, 트랜잭션, 영수증 트리의 루트 노드 값을 가지고 있기 때문에, 네트워크의 노드들은 모든 상태를 저장하고 있지 않더라도 이더리움의 상태 일부분을 검증할 수 있다.
이더리움이 트랜잭션 기반 상태 머신이라는 것을 배웠다. 다시 말해, 계정 간에 일어난 트랜잭션은 이더리움의 전역 상태를 하나의 상태에서 다음 상태로 이동시킨다는 것이다.
기본적으로 트랜잭션은 EOA에서 생성되고, 일련의 과정을 거쳐 블록체인에 올라간 "암호화 서명된 명령어의 집합"이다.
그 외의 트랜잭션은 "메시지 호출(message call)"과 "컨트랙트 생성(contract creations)" 두 종류로 나누니다. 즉, 트랜잭션은 외부 세계를 이더리움 내부의 상태로 연결해주는 다리와 같다. 그리고 연결의 창구가 바로 CA인 것이다.
그렇다고 해서 CA가 다른 CA과 전혀 연관 없다는 것은 아니다. 이더리움 전역 상태에 있는 CA는 "메시지"나 "내부 트랜잭션"을 통해 다른 CA와 상호작용 할 수 있다. 메시지와 내부 트랜잭션은 일반적인 트랜잭션과 비슷하지만, EOA에서 생성되지는 않으며, 오로지 CA에게서만 생성된다. 또한 일반적인 트랜잭션과는 다르게, 메시지와 내부 트랜잭션은 EVM에만 존재하는 가상 객체이다.
한 CA가 다른 CA에 내부 트랜잭션을 보내면, 수신하는 CA에 있는 관련 코드가 실행된다.
트랜잭션의 두 종류 중 "컨트랙트 생성(contract-creating)" 트랜잭션을 알아보자.
새로운 CA를 만들기 위해서는 먼저 특별한 공식을 이용해 새로운 계정의 주소를 정의해야 한다. 그리고 다음의 과정을 통해 새로운 CA를 만들 수 있다.
한번 계정을 초기화하고 나면, 트랜잭션에서 보낸 init 코드를 사용해 새로운 CA를 생성할 수 있다. 이 init 코드가 실행되는 동안에는 다양한 일이 발생한다. 계약 생성자에 따라 CA의 스토리지를 업데이트하거나, 다른 CA를 새롭게 생성하거나, 다른 메시지 호출을 만들 수 있따.
계약을 초기화하는 이 init 코드를 실행할 때는 가스를 사용한다. 트랜잭션은 남아있는 가스보다 더 많은 가스를 소비할 수 없기 때문에, 만약 남아있는 가스를 다 사용한 경우 OOG(Out-of-Gas) 예외처리와 함께 코드 실행이 종료된다. OOG로 인한 트랜잭션 종료가 발생하면 상태는 트랜잭션 실행 이전 상태로 돌아간다.
이더리움에서는 트랜잭션에 있는 연산을 수행할 때마다 비용을 청구하는데, 이 비용의 단위를 가스(gas)라고 한다. 트랜잭션을 보낼 때는 코드에 처리량 만큼의 가스를 함께 보내야 한다.
트랜잭션이 실패해도 트랜잭션 송신자는 소진된 가스를 환불받을 수 없다. 그러나 송신자가 트랜잭션과 함께 이더 값을 보낸 경우, 이더는 환불된다.
init 코드가 성공적으로 실행되면, 마지막으로 CA 코드에 대한 비용이 지불된다. 이 비용은 스토리지 비용이며, 생성된 CA 코드의 크기에 비례한다. 만약 CA 코드에 대한 비용을 지불할 가스가 남아있지 않은 경우, 트랜잭션은 OOG 예외와 함께 중단된다. 예외 없이 트랜잭션 생성이 완료된 경우, 미사용된 가스는 송신자에게 환불되고, 변경된 상태가 저장된다.
메시지 호출을 실행하는 것은 CA 생성과 비슷하지만, 몇가지 다른 점이 있다.
새 계정이 생성되지 않기 때문에 메시지 호출 실행에는 init 코드가 포함되지 않는다. 그러나 입력 데이터를 가지고 있을 수는 있다. 메시지 호출은 한번 실행되면 출력 데이터를 포함한 추가적인 요소들을 가지게 될 수도 있는데, 이 추가 요소들은 뒤에 이어질 다른 실행에 필요한 데이터이다.
CA 생성과 마찬가지로, 메시지 호출을 실행할 때 가스가 부족하거나, 트랜잭션이 잘못되어 종료되는 경우(예를 들어, 스택 오버플로우가 발생하거나, jump 실행의 결과가 올바르지 않거나, 잘못된 명령어를 사용하는 경우) 사용된 가스는 환불되지 않는다. 남은 가스는 모두 소비되며, 상태는 직전 상태로 돌아간다.
그렇다면 트랜잭션은 실제로 EVM에서 어떻게 동작할까?
트랜잭션 처리를 실제로 처리하는 프로토콜은 EVM이다. EVM의 구성 요소를 하나씩 살펴보자.
EVM이 솔리디티 코드를 컴파일한 바이트코드를 받으면 실행이 시작된다. 맨 처음에는 메모리와 스택은 비어있으며, 프로그램 카운터(연산 실행 횟수)는 0이다.
PC(Program Counter): 0
Stack: []
Memory: []
Storage: {}
EVM은 트랜잭션을 반복적으로 실행하며, 각 사이클에서 시스템 상태(이더리움의 전역 상태)와 머신 상태(machine state)를 계산한다. 머신 상태는 다음과 같이 구성된다.
코드가 실행되면서 스택에 있는 요소들은 왼쪽에서부터 순서대로 추가되거나 제거되며, 남은 가스에서 적절한 양의 가스가 제거되고, 프로그램 카운터가 올라간다.
매 사이클이 끝날 때 다음과 같은 세가지 케이스가 발생할 수 있다.
머신이 정상적으로 실행을 마치면, 결과적으로 발생한 상태와 남아있는 가스, 발생한 세부 상태, 결과값을 생성한다.
이더리움에서 트랜잭션의 결과로 발생하는 모든 연산은 수수료를 요구한다. 이 수수료를 가스(gas)라고 한다
가스는 트랜잭션 코드에 있는 모든 opcode를 실행하는데 필요한 수수료를 측정하는 데 사용하는 단위이다. Gas price은 가스 당 지불하려고 하는 이더의 양을 의미하며, Gwei라는 단위를 사용한다.
1 이더 = 10^18 wei 이며, 1 Gwei= 10^9 wei
트랜잭션 송신자는 gas limit과 gas price를 트랜잭션에 지정한다. 이 gas price와 gas limit은 송신자가 트랜잭션을 실행하는데 지불하고자 하는 Wei의 최대 양을 의미한다.
예를 들어, 송신자가 gas limit을 50,000으로 지정하고, gas price를 20 gwei로 지정했다고 가정해보자. 이것은 송신자가 최대 50,000 x 20 gwei = 10^15 Wei = 0.001 이더
를 트랜잭션을 실행하는 데 지불한다는 것을 의미한다.
만약 CA에 이 최대치를 지불하고도 남을 충분한 이더가 들어있다면 트랜잭션은 문제없이 실행될 것이다. 송신자는 트랜잭션이 완료된 후, 사용되지 않은 가스를 기존 비율로 환불받는다.
Gas limit - use gas(50) - use gas(30) = remaining gas
반대로 송신자가 트랜잭션을 실행할만한 충분한 가스를 제공하지 않은 경우, 트랜잭션은 OOG 상태가 되며, 실행이 중지된다. 상태가 변경되었다면, 트랜잭션이 실행되기 이전의 상태로 돌아간다. 또한 실패한 트랜잭션에 대한 기록이 남으며, 이 기록에서 어떤 트랜잭션이 시도되었고 어디에서 실패했는지 남게 된다. 그리고 EVM은 트랜잭션 실행이 중지되기 전까지 연산을 수행했기 때문에, 연산에 사용된 가스는 환불되지 않는다.
이 가스는 채굴자의 주소로 송금된다. 채굴자는 연산을 수행하고 트랜잭션을 검증하는 데에 자원을 소모하기 때문에 이에 대한 보상으로 가스 수수료를 받는다.
연산을 할 때 뿐만 아니라, 스토리지를 사용할 때도 수수료를 내야 한다. 스토리지의 최종 수수료는 32 바이트 단위에 비례한다.
스토리지 수수료는 일반적인 수수료와는 다르게 약간 특이한 부분이 있다. 가령, 스토리지가 증가하면 모든 노드에서 이더리움 상태 데이터베이스의 크기가 커지기 때문에 저장되는 데이터의 양을 최대한 적게 유지할 수록 인센티브를 부여한다. 이러한 이유 때문에 만약 트랜잭션이 스토리지에 있는 특정 요소를 지우는 연산을 수행하면, 해당 연산을 수행하는 데에 대한 수수료는 면제하고, 저장 공간을 확보했기 때문에 기존에 요소를 스토리지에 추가했을 때 지불했던 가스를 환불받는다.
예를 들어, 스토리지에 요소 하나를 할당했을 때 100 gas를 지불해야 한다고 가정해보자. 트랜잭션에서 스토리지에 요소 하나를 할당하여 100 gas를 지불했다. 이후 해당 요소를 다시 스토리지에서 제거하는 연산을 수행할 때는, 제거 연산에 대한 수수료는 0 gas이며, 더이상 스토리지 자리를 차지하지 않기 때문에 할당 할 때 들었던 100 gas를 환불해준다.
이더리움은 비트코인과는 달리, 스마트 컨트랙트에서 수행되는 모든 연산에 대해 가스라는 수수료를 부과합니다. 이렇게 수수료를 부과하는 이유는 이더리움 네트워크 위에서 동작하는 스마트 컨트랙트 작업들이 전체 노드에 영향을 미치기 때문이다.
이더리움은 튜링 완전 언어이기 때문에, 튜링 불완전한 비트코인 스크립트와는 달리 반복문을 지원한다. 그러나 반복문의 가장 큰 단점은 무한 루프 문제가 일어날 수 있다는 것이다. 만약 악의적인 사용자가 큰 자원을 소모하는 무한 루프 코드가 들어있는 트랜잭션을 실행시킨다면, 트랜잭션은 이더리움 네트워크의 엄청난 양의 자원을 소모하게 될 것이고, 심각하게는 이더리움 네트워크 전체가 멈출 수도 있다.
수수료는 이러한 악의적인 공격으로부터 네트워크를 보호한다. 각 연산마다 수수료를 부과하면 악의적인 사용자라도 수수료를 지불해야 하기 때문에 쉽게 무한 루프 코드를 실행시킬 수 없다. 또한 의도치 않게 무한 루프를 만든 코드를 실행시키더라도 gas limit을 설정해두면 지정해둔 가스를 다 소모하기 전에 실행을 멈출 수 있다.
이전 포스팅 들에서 계속 언급 하였듯, 프라이빗 블록체인은 네트워크 내에서 초대된 사용자만이 네트워크에 참여하고, 원장에 접근할 수 있는 허가형(permissioned) 블록체인을 의미한다.
프라이빗 블록체인은 채굴 과정과 합의 알고리즘에 대한 권한을 중앙 기관이 개발하고 유지한다. 중앙 기관은 네트워크에 참여할 수 있는 사용자를 결정한다.
프라이빗 블록체인중 대표적인 플랫폼인 하이퍼레저 패브릭(hyperledger fabric)에 대해 알아보자.
하이퍼레저 패브릭은 분산 원장 솔루션을 제공하는 플랫폼으로, 기밀성, 탄력성, 유연성 및 확장성을 제공한다. 다른 블록체인 솔루션과 구별되는 가장 큰 특징은 다른 하이퍼레저 기술을 플러그 앤 플레이 방식으로 지원하는 유연하고 탄력적인 아키텍처를 제공한다는 점이다.
플러그앤 플레이 방식 : 꽂으면 실행된다는 뜻으로, 컴퓨터 실행 중에 주변 장치를 부착해도 별다른 설정 없이 작동한다.
피어(Peer)
피어는 블록체인 네트워크의 기본 요소이며, 원장과 스마트 컨트랙트를 호스팅하는 노드이다. 어플리케이션이 체인 코드(스마트 컨트랙트)를 실행하여 쿼리를 하거나 원장을 업데이트하도록 한다.
네트워크의 각 피어에는 디지털 인증서가 할당되며, 디지털 인증서는 피어가 참여하는 채널에 해당 피어를 식별하도록 해준다. 이 방식으로 인해 피어는 각 채널에서 다양한 권한을 가질 수 있다.
모든 피어는 기본적으로 동일하지만, 네트워크에서 여러 역할을 수행할 수 있다.
채널(channel)
하이퍼레저 패브릭 채널은 둘 이상의 특정 네트워크 노드 간의 통신을 위한 프라이빗 서브넷으로, 프라이빗 트랜잭션을 수행하기 위한 목적으로 사용된다.
채널은 멤버(orgs), 공유 원장, 체인 코드 어플리케이션 및 정렬 서비스 노드에 의해 정의된다. 네트워크의 모든 트랜잭션은 채널에서 실행되며, 각 당사자는 해당 채널에서 트랜잭션을 수행하기 위해 인증을 거쳐야 한다. 채널에 참여하는 각 피어는 MSP(Membership Service Provider)가 제공하는 고유한 식별자를 가지며, 각 피어를 해당 채널과 서비스에 인증한다.
스마트 컨트랙트는 원장에 추가되는 새로운 데이터를 생성하는 실행 가능한 로직을 정의한다. 일반적으로 스마트 컨트랙트와 체인코드라는 용어는 혼용되지만, 스마트 컨트랙트는 전역 상태에 포함된 비즈니스 객체의 상태를 변경시키는 트랜잭션 로직을 정의한다. 반면, 체인코드는 블록체인 네트워크에 배포하기 위해 패키징된 스마트 컨트랙트의 집합이다. 즉, 스마트 컨트랙트는 트랜잭션을 관리하며, 체인 코드는 스마트 컨트랙트를 배포하기 위해 패키징하여 관리한다.
체인 코드를 통해 아키텍트나 개발자가 하나의 블록체인 네트워크 안에 있는 서로 다른 조직 간 공유되어야 하는 데이터와 비즈니스 프로세스를 정의한다
보증(endorsement)
모든 체인 코드에는 모든 스마트 컨트랙트에 적용되는 관련 보증 정책이 있다. 이 정책은 트랜잭션의 유효성을 위해 트랜잭션에 서명을 해야 하는 블록체인 네트워크 내 조직들을 의미한다.
유효한 트랜잭션
스마트 컨트랙트는 블록체인 네트워크 내 조직이 소유한 피어 노드에서 실행된다. 이 스마트 컨트랙트들은 트랜잭션 제안서라 불리는 입력 파라미터의 집합을 취하여 스마트 컨트랙트의 프로그램 로직과 함께 사용하여 원장을 읽고 쓴다. 전역 상태의 변화는 읽은 상태와 트랜잭션이 유효할 경우 기록될 새로운 상태를 모두 포함하는 읽기-쓰기 집합으로 구성된 트랜잭션 제안 응답으로 캡쳐된다. 전역 상태는 스마트 컨트랙트가 실행될 때 변하지 않고, 트랜잭션이 최종적으로 유효해지면 업데이트 된다.
네트워크의 모든 피어 노드에 배포되는 트랜잭션은 두 단계에 거쳐 검증된다. 먼저, 보증 정책에 따라 지정된 조직에서 서명된 거래인지 확인한다. 그리고 전역 상태의 현재 값이 보증 피어 노드에 의해 서명되었을 때의 트랜잭션의 읽기 집합과 일치하는지 확인한다. 트랜잭션이 두 과정을 모두 통과하면 유효한 트랜잭션이 된다.
유효한 트랜잭션과 유효하지 않은 트랜잭션은 모두 블록체인 이력에 추가되지만, 유효한 트랜잭션만이 전역 상태를 업데이트한다.
탈중앙화 금융는 스마트 컨트랙트를 활용해 금융 시장, 옵션, 스테이블 코인, 거래소, 자산 관리 등 전통적인 금융 상품과 서비스를 재창조하고, 여러 서비스를 결합하여 새로운 금융 원형을 만들어내는 어플리케이션이다.
블록체인 기반 게임은 게임 내 액션의 변조 방지를 위해 스마트 컨트랙트를 사용한다. 한가지 예로, RPG 게임이나 한정판 NFT에서 스마트 컨트랙트를 이용해 모든 사용자가 희귀한 디지털 자산을 얻을 수 있는 공정한 배포 모델을 가질 수 있다.
DAO는 컴퓨터 프로그램으로 인코딩 되고, 조직 구성원들이 통제하며, 중앙 정부의 영향을 받지 않는 규칙들로 구성된 탈중앙화 자율조직으로, 영리 및 비영리 기업을 조직하기 위한 새로운 분산형 비즈니스 모델을 제공한다. 규칙은 코드에 내장되어 있기 때문에 관리자가 필요하지 않으며, 따라서 관료제나 계층적 구조가 없다.
ICO는 기업 공개(IPO)와 비슷한 개념으로, 새로운 암호화폐를 만들기 위해 투자자들에게 투자금을 받고, 그 댓가로 코인을 나눠주는 것을 의미한다.
브릿지는 토큰이나 임의의 데이터를 하나의 체인에서 다른 체인으로 전송할 수 있게 하는 연결을 의미한다. 두 체인은 서로 다른 프로토콜, 규칙, 거버넌스 모델을 가질 수도 있지만, 브릿지는 양 쪽에서 안전하게 상호 운용할 수 있는 호환가능한 방법을 제공한다.
일반적으로 스마트 컨트랙트가 빠르게 도입되고 있는 분야는 세 경우가 있다.
보험업에서 특정 조건을 만족시키면 계약에 대한 보상금이 지급되도록 스마트 컨트랙트를 작성하여, 조건 성취 시 자동으로 보험금을 지불하도록 한다.
사례: 보험
집이나 자동차를 공유하는 경우, 계약 조건을 정하고 이에 따라 금전을 지급하거나 서비스를 제공하도록 한다. 가령, 집을 일정 시간 빌리는 경우, 스마트 컨트랙트에 비용을 지불하면 자동으로 집 문이 열려 사용가능하도록 한다.
사례: 자동차 렌탈 서비스
유통에 참여하는 당사자들(생산자, 공급자, 운영자, 배급업체, 유통업체, 규제당국, 소비자 등)이 유통 물품에 대한 정보를 확인하고, 계약 조건이 만족되면 스마트 컨트랙트 상에서 대금을 지불한다.
사례: 물류 유통
Binded에서는 온라인에서 유통되는 저작권 있는 작품을 사용자가 업로드하면, 소유증명서를 발급하고, 사용자 정보를 저장하여 저작권을 요구할 수 있는 서비스를 제공한다.
오라클은 web API나 마켓 데이터 피드와 같은 방식을 통해 블록체인과 스마트 컨트랙트용 외부 데이터를 검색하고 검증하는 것을 의미한다. 스마트 컨트랙트에서 요구하는 데이터 종류에는 가격 정보, 날씨 정보, 또는 게임을 위한 난수 생성 등이 있을 것이다.
오라클은 특정 정보에 대해 데이터 자원을 쿼리하고, 블록체인과 외부 데이터 간의 인터페이스를 제공하는 것으로 구성된다. 그 결과, 스마트 컨트랙트는 외부 데이터로부터 오는 특정 정보에 의해 실행될 수 있다. 중요한 것은 외부에서 오는 데이터는 블록체인이나 스마트 컨트랙트처럼 결정적(deterministic)이지 않다는 점이다. 오라클은 외부에서 오는 비결정적인 정보를 블록체인이 이해하고 특정 조건에서 실행할 수 있도록 하는 형식으로 만들어주는 역할을 한다.
오라클 문제는 서드 파티 오라클과 스마트 컨트랙트의 무신뢰성 실행 간 보안, 인증, 신뢰 충돌 문제에 관한 것이다. 스마트 컨트랙트는 주어진 데이터에 따라 계약 이행 여부를 결정할 뿐이지, 데이터에 대한 자체적인 판단 능력은 가지고 있지 않다. 즉, 스마트 컨트랙트로 들어오는 데이터에 대한 출처나 신뢰도가 불명확할수 있다는 것이다. 출처가 분명하더라도 외부 데이터를 가져오는 과정에서 해커에 의해 위변조가 일어날 수도 있다. 이러한 문제로 인해 데이터 자체에 대한 신뢰성에 의문이 제기될 수도 있다.
대표적인 오라클 문제로는 난수 생성 문제가 있다.
우리는 복권, 게임을 만들거나, 테스트 데이터를 얻기 위해 난수를 만들고 싶을 때가 있다. 특히나 이더리움의 등장으로 블록체인 상에서 다양한 dApp을 만들게 되면서 난수에 대한 필요성은 더욱 높아졌다.
그러나 컴퓨터 과학에서 난수를 생성하는 것은 단순히 주사위를 던지는 것보다 더 어려운 문제이다. 컴퓨터는 입력받은 값에 대해 정해진 행동을 하기 때문에, 조종할 수 없는 새로운 데이터를 생성하는 것을 잘 하지 못한다. 따라서 컴퓨터 과학에서 난수는 "유사 난수(Pseudo Random)"와 "진짜 난수(True Random)"로 나뉜다.
유사 난수는 컴퓨터에 있는 데이터와 알고리즘을 통해 얻게 되는 난수이다. 이 난수는 컴퓨터 내부에 있는 알고리즘과 데이터를 가지고 생성되었기 때문에 특정한 패턴을 가지게 되고, 이로 인해 예측할 수 있다. 진짜 난수는 외부로부터 불확실성을 가져와 생성한 난수 값이다. 예를 들어, 외부 소음을 가져와 디지털화 하여 난수를 생성하면 무작위성을 확보할 수 있따. 컴퓨터 외부에서 값을 가져왔기 때문에 조작할 수 없으며, 따라서 예측할 수도 없다.
일반적으로 우리가 알고 있는 난수 생성 알고리즘은 컴퓨터 내부에 있는 데이터와 알고리즘을 이용한 유사 난수 생성기이다. 결과가 편향되지는 않지만, 결과값을 어느정도 예측할 수 있고, 조종할 수도 있기 때문에 진짜 난수는 아니다.
블록체인에서 난수를 생성하는 방식에는 다음과 같은 방식을 떠올릴 수 있다.
블록체인에서는 모든 노드들이 트랜잭션을 검증함으로써 데이터의 무결성을 보장하기 때문에, 블록체인에서 일어나는 모든 동작은 결정적인 방식으로 일어난다. 예를 들어, 이더리움 스마트 컨트랙트에는 랜덤 함수가 구현되어 있지 않다. 노드들이 블록을 검증하기 위해서는 블록에 있는 트랜잭션을 실행한 결과 상태값과 현재 자신이 가지고 있는 상태값을 비교해야 한다. 그런데 트랜잭션에서 무작위 값이 나오면 트랜잭션을 실행한 결과 상태값이 매번 변할 것이기 때문에 데이터의 무결성을 증명하기가 매우 어려워진다.
그러나 우리가 원하는 난수 생성은 결정적이면 안됩니다. 매번 예측할 수 없는 값이 나와야 예측하거나 조작할 수 없기 때문이다. 그러나 블록체인에서는 진짜 난수를 만들 수 없기 때문에 난수 생성 과정에서 나온 값이 진짜 무작위 값이라는 것을 보장할 수 없다.
따라서 블록체인에서 조작하거나 예측할 수 없는 진짜 난수를 만들 때는 이 난수값이 "정말로 조작되지 않았는지" 증명할 수 있어야 한다.
1. Commit Reveal Scheme
기존의 "여러 사용자가 가져온 값을 사용하는 경우"를 개선한 방식이다. 기존에는 서로가 서로의 값을 알 수 있었지만, 스마트 컨트랙트를 사용하면 서로의 값을 모른 채로 값을 제출할 수 있다.
먼저 Commit Stage에서는 자신이 보내려는 값을 암호화하고 일정 금액의 토큰과 함께 스마트 컨트랙트에 보낸다. 토큰은 예치금의 역할을 한다.
그리고 일정 기간이 지나면 참여자들은 자신이 제출하려고 했던 원래 값을 스마트 컨트랙트에 보낸다. 스마트 컨트랙트는 사용자가 보낸 원래 값을 암호화하여, 이전에 보낸 암호화된 값과 비교한다. 만약 다른 값을 보냈다면 이 유효성 검사를 통과하지 못하게 된다. 사용자가 올바른 값을 보냈다면 토큰을 돌려준다. 그리고 스마트 컨트랙트는 주어진 값들을 사용해 난수를 생성한다.
Commit Reveal Scheme을 사용하면 서로가 어떤 값을 보내는지 모르기 때문에 난수 값을 예상하거나 난수 생성을 조작할 수 없을 뿐더러, 유효성 검사 과정을 통해 난수값을 조작할 수 없다는 것을 증명할 수 있다.
2. BLS Scheme
임계값 서명의 한 종류이다. BLS Scheme에서는 N명의 사용자가 개인키 S를 쪼개어 가지고 있다가, 난수를 생성해야 할 때 참여자들이 각자 자신이 가지고 있는 개인키 조각을 제출한다. 이 때 개인키를 k명 이상이 제출하면 k개의 개인키 조각을 가지고 난수를 생성한다.
Commit Reveal Scheme은 한 두명이 부정직해도 난수 생성이 불가능해질 수 있지만, BLS Scheme은 k명의 사람이 정직하면 난수를 생성할 수 있다는 장점이 있다.