WARNING!
- 학습 목적의 백서 번역 포스팅입니다.
- 지식이 부족하거나 애매한 부분은 Ethereum Github Whitepaper (Kor)를 참고하여 번역했습니다.
- 이더리움 백서를 처음 접하시는 분은 위 링크의 백서를 보시는 것을 추천드립니다.
- 생소한 개념에 대해서는 참고가 가능한 링크를 덧붙였습니다.
- 학습 목적으로는 번역 작업이 효율적이지 않네요. 백서 번역 시리즈는 여기까지만!
Table of contents
- Ethereum
- Ethereum Accounts
- Messages and Transactions
- Ethereum State and Transition Function
- Code Execution
- Blockchain and Mining
Ethereum
이더리움의 개발 목적은 탈중앙집중식 어플리케이션 개발을 위한 대체 프로토콜을 만드는 것이다. 이더리움은 대규모 탈중앙집중식 어플리케이션 클래스에 매우 유용할, 여러 가지의 tradeoff 선택지를 제공하며, 다음과 같은 상황에 특화되어 있다:
- 짧은 개발기간
- 소규모/ 이용량이 적은 어플리케이션 보안
- 복수의 어플리케이션 간의 효율적인 상호작용
이더리움은 튜링완전 프로그래밍 언어를 내장한 블록체인이라는 추상화 기초 계층을 구축함으로써 위 장점을 제공한다. 누구나 이더리움을 통해 임의의 소유권 규칙이나 계약 형식, 상태 변환 함수가 담긴 스마트 컨트랙트나 탈중앙집중식 어플리케이션을 작성할 수 있다. Namecoin의 프로토타입은 오직 코드 두 줄로 작성 가능하고, 화폐나 평판 시스템과 같은 기타 프로토콜은 코드 스무 줄 아래로 작성할 수 있다. 특정 기준이 만족되는 경우에만 담고 있는 가치를 꺼낼 수 있는 암호화된 "상자", 스마트 컨트랙트는 이더리움 플랫폼 위에서 비트코인 스크립팅보다 훨씬 강력하게 작성될 수 있다. 강력한 스마트 컨트랙트는 블록체인의 단점을 그대로 개선해낸, 이더리움의 아래 강점들로부터 기인한다:
- 튜링 완전 (Turing-completeness)
- 가치 인지 (value-awareness)
- 블록체인 인지 (blockchain-awareness)
- 상태 ( state)
Ethereum Accounts
이더리움에서 상태는 "어카운트"라는 오브젝트로 구성되며, 각 어카운트는 "20바이트 어드레스", "값을 직접 전달하는 상태 변환", "어카운트 간 관계정보"를 갖는다. 이더리움 어카운트는 4개의 필드로 구성된다:
- 넌스(nonce) : 각 거래가 딱 1번만 처리되었는지를 확인하기 위한 카운터
- 어카운트의 현재 이더( ether) 잔고
- 어카운트의 컨트랙트 코드 (존재할 경우)
- 어카운트의 저장 공간 (디폴트 = 비어있음)
"이더(ether)"는 이더리움에서 주요한 내부 암호화의 연료 (crypto-fuel) 역할을 수행하고, 거래 비용을 지불하는 데 사용된다. 일반적으로 이더리움에는 두 가지 종류의 어카운트가 존재한다:
- 외부 소유 어카운트
- 프라이빗 키가 제어하는 어카운트
- 코드가 존재하지 않는다
- 거래의 생성/서명을 통해, 외부 소유 어카운트로부터 사용자는 "메시지"를 전송할 수 있다
- 컨트랙트 어카운트
- 컨트랙트 코드가 제어하는 어카운트
- 컨트랙트 어카운트가 메시지를 수신할 때마다 어카운트 코드가 실행
- 내부 스토리지를 읽고 쓸 수 있게 된다
- 다른 메시지를 전송하거나 수신받은 메시지에 대해 컨트랙트를 생성한다
이더리움의 "컨트랙트(contract)"은 "만족"되거나 "준수"되어야 하는 무언가라기 보다는, 이더리움 실행 환경 내부에서 살아 움직이는 "자율 에이전트"에 가깝다는 점도 짚고 넘어가자. 컨트랙트는 자율 에이전트로서, 메시지(message)나 트랜잭션(transaction)과 맞닥뜨릴 때마다 특정 코드를 실행하며, 영구 변수를 추적 관리하기 위해 이더 잔고와 키/이더 저장소를 직접 제어할 수 있다.
용어의 혼란을 피하기 위하여, 비트코인의 transaction을 지난 포스팅 이더리움 백서 한글번역본 Part 1. ~History, Ver1.0@choigww에서 문맥에 따라 "거래"로 번역하였으며, 이하 이더리움의 transaction은 국문 표기인 "트랜잭션"으로 번역했습니다.
state의 경우 동일하게, "상태"로 번역했습니다.
메시지, 컨트랙트 등기타 용어의 경우도 어색함을 피하기 위하여 전부 국문 표기로 통일하여 사용하겠습니다. @choigww
Messages and Transactions
이더리움의 "트랜잭션(transaction)"는 외부 소유 어카운트가 전송하는 메시지를 담기 위한, 서명된 데이터 패키지(signed data package)를 지칭한다. 트랜잭션은 다음과 같은 값/데이터를 담고 있다:
- 메시지 수신자
- 송신자를 식별하는 서명
- 송신자가 수신자에게 보내려는 일정량의 이더
- 추가 데이터 필드
STARTGAS
필드- 트랜잭션 실행에서 허용되는 최대 컴퓨터 연산 단계의 수
GASPRICE
필드- 컴퓨터 연산 단계마다 송신자가 지불하는 수수료
상단 3개는 암호화폐의 기본적인 항목이다. 데이터 필드는 기본적으로 어떤 함수도 없지만, 가상 머신은 컨트랙트가 데이터에 접근할 때 사용하는 수행 명령 코드(opcode/operation code)를 갖추고 있다. 예를 들면, 컨트랙트가 블록체인 연동 도메인 등록 서비스로서 작동 중이라면, 두 개의 "필드"를 가진 인풋 데이터(등록할 도메인 + IP 주소)를 해석하고자 할 것이다. 그러면 컨트랙트는 메시지 데이터로부터 이 값을 읽어들여 저장소에 적절하게 배치할 것이다.
STARTGAS
와 GASPRICE
필드는 이더리움을 DoS 공격으로부터 지키는 핵심적인 요소다. 우연 또는 악의로 인해 발생한 무한 루프 또는 다른 컴퓨터 연산 소모를 막기 위해서, 각 트랜잭션은 컴퓨터 연산 단계를 몇 회나 사용할 수 있는지에 대한 제한값을 요구한다. 또한 트랜잭션 데이터 1바이트마다 5 가스(gas)의 수수료가 붙는다. 수수료 시스템의 목적은 공격자로 하여금 그들이 소모하는 자원에 비례한 대가를 지불하도록 하는 것이다. 이를테면 컴퓨터 연산, 주파수 대역, 저장소 같은 자원을 사용한 비용이다. 따라서, 이러한 자원을 마구 사용해서 네트워크를 주도하는 트랜잭션은 자원의 소모에 비례한 가스 수수료를 보유하고 있어야 한다.
Messages
컨트랙트는 다른 컨트랙트에 "메시지"를 전송할 수 있다. 메시지는 전송 가능한 형태로 변환되지 않는(직렬화되지 않는) 가상 객체며 이더리움 실행 환경에서만 존재한다. 메시지는 다음과 같은 정보를 담는다:
- 메시지 전송자 (필수)
- 메시지 수신자
- 메시지와 함께 전송되는 일정량의 이더
- 추가 데이터 필드
STARGAS
필드
본질적으로 메시지는 트랜잭션과 유사하다(트랜잭션은 컨트랙트에 의해서만 생성된다는 점만 제외하면). 메시지는 컨트랙트가 실행 코드를 돌리다가, 메시지를 만들고 실행하는 'CALL'
수행 명령 코드를 호출함으로써 생성된다. 트랜잭션과 비슷하게, 메시지를 받은 수신자 어카운트는 자신의 코드를 실행하게 된다. 그러므로, 컨트랙트는 외부 개체와 완전하게 동일한 방식으로, 다른 컨트랙트와 관계를 맺을 수 있다.
트랜잭션이나 컨트랙트가 할당한 가스 허용량은 트랜잭션과 모든 부가적인 코드의 실행으로 소비된 가스 총량에 대해 적용된다. 가령 외부 개체 A가 B에게 1000 가스와 함께 트랜잭션을 보내고, B가 C에게 메시지를 보내기 전 600 가스를 소비했으며, C의 내부 실행에 300 가스가 소비된 뒤 반환된다면, B는 남은 100 가스를 사용할 수 있다.
Ethereum State Transition Function
이더리움의 상태 변환 함수 APPLY(S, TX) -> S'
는 아래와 같이 정의 가능하다:
1. 아래 내용을 확인하고, 하나라도 실패하면 에러 반환
If NOT 트랜잭션이 잘 만들어짐 (값의 개수 등): return error;
If NOT 유효한 서명: return error;
If NOT 넌스가 전송자 어카운트의 넌스와 일치: return error;
2. 트랜잭션 수수료 계산 ( = STARTGAS * GASPRICE)
서명으로부터 전송자 주소 결정, 확인
전송자 어카운트 잔고 = (전송자 어카운트 잔고 - 트랜잭션 수수료)
If NOT 잔고 >= 0: return error;
전송자 넌스 값 ++
3. 변수 GAS를 STARGAS로 초기화 : GAS = STARGAS
트랜잭션 바이트에 상응하는 수수료로 가스 일정량을 차감
( GAS = GAS - (NUM_TRANSACTION_BYTES * GAS_PER_BYTE) )
4. 전송자 어카운트에서 수신자 어카운트로 트랜잭션 값 전송
If 수신자 어카운트가 존재하지 않는 경우: 수신자 어카운트 생성
If 수신자 어카운트가 컨트랙트인 경우: 컨트랙트 코드 실행 (가스 고갈 시, 실행 중단)
5. If (전송자의 화폐가 부족) OR (코드 실행을 위한 가스 부족):
수수료 지불을 제외한 모든 변경사항 원상복구
채굴자 어카운트에 수수료 추가
else: (화폐 / 가스 부족 없이, 상태 전환 완료)
남은 가스에 대한 수수료를 전송자에게 환불
소비된 가스에 대한 수수료를 채굴자에게 전송
예를 들어, 컨트랙트 코드가 아래와 같다고 하면:
if !self.storage[calldataload(0)]:
self.storage[calldataload(0)] = calldataload(32)
실제로는 컨트랙트 코드는 저레벨 EVM (Ethereum Virtual Machine) 코드로 작성된다. 위 예시는 편의를 위해 고수준 언어 Serpent로 작성된 것이며, EVM 코드로 컴파일 가능하다.
컨트랙트 저장소가 빈 상태로 시작해 트랜잭션이 다음 데이터로 구성될 경우:
- 10 이더
- 2000 가스
- 0.001 이더 가스프라이스(gas-price)
- 64 바이트의 데이터
- 숫자값
2
( 0 - 31 바이트 ) - 문자열
CHARLIE
( 32 - 63 바이트 )
- 숫자값
이 트랜잭션에 대한 상태 변환 함수 프로세스는 다음과 같다:
- 트랜잭션이 유효하며 정상적으로 만들어졌는지 확인한다.
- 트랜잭션 전송자가 최소한 2000(gas) * 0.001(gas-ether price) = 2 이더를 가지고 있는지 확인한다. 2이더를 갖고 있다면, 전송자 어카운트로부터 2 이더를 차감한다.
- 가스 변수를 초기화(
gas = 2000
). 트랜잭션의 길이는 170 바이트이며 바이트 당 수수료는 5 가스라고 가정하고, 850 가스를 차감하여 1150 가스가 남는다. - 전송자 어카운트로부터 10 이더를 차감하고, 컨트랙트 어카운트에 10 이더를 더한다.
- 지정 코드를 실행한다:
*인덱스2
에 위치한 컨트랙트 스토리지가 사용되었는지 확인
If 사용되지 않았다면: 인덱스2
스토리지에CHARLIE
문자열을 할당한다.
*위 코드 수행에 187 가스가 쓰이고 현재 남은 가스는 1150 - 187 = 963 가스. - 963 (remaining gas) * 0.001 (gas-ether price) = 0.963 이더를 전송자 어카운트로 다시 전송하고, 최종 결과를 반환한다.
트랜잭션 끝단에 컨트랙트가 없다면, 총 트랜잭션 수수료는 GASPRICE
* 트랜잭션 바이트의 길이와 동일하며, 트랜잭션과 함께 전송한 데이터는 아무런 역할도 하지 않는다.
마찬가지로, 메시지 또한 트랜잭션에 대해서 상태를 원복시킨다:
- 메시지 실행 중 가스가 고갈되면, 메시지의 실행과 그로 인해 시작된 다른 모든 코드의 실행은 원 상태로 돌아간다.
- 그러나 부모 실행 내역은 복구되지 않는다.
- 이는 마치 A가 B를 G 가스로 호출하면 A의 실행은 아무리 많아도 최대 G 만큼의 가스만 잃을 수 있듯, 컨트랙트가 다른 컨트랙트를 호출하는 것은 "안전"함을 의미한다.
마지막으로, 컨트랙트를 생성하는 수행 명령 코드는 CREATE
다. CREATE
수행 명령 코드의 실행 메커니즘은 CALL
과 유사하지만, 실행의 결과(아웃풋)가 새롭게 생성된 컨트랙트의 코드를 결정한다는 점에서 차이가 있다.
Code Execution
이더리움 컨트랙트의 코드는 "이더리움 가상 머신 코드" 또는 "EVM 코드"로 불리우는 저레벨 스택 기반 바이트코드 언어로 작성된다. 코드는 일련의 바이트로 구성되며 각각의 바이트는 연산 내용을 담는다. 일반적으로 코드 실행은 연산을 할 때마다 프로그램 카운터를 증가시키면서 코드의 끝에 도달하거나 오류, STOP
, RETURN
구문이 나올 때까지 연산을 무한히 반복하는 루프를 말한다. 연산은 데이터를 저장하는 세 종류의 저장소에 접근할 수 있다:
- 스택(stack) - 값을 push or pop하는 last-in-first-out 저장소
- 메모리 - 무한히 확장 가능한 바이트 어레이
- 컨트랙트의 장기 저장소(storage) - 키/값의 쌍 (key/value pair)
- 연산 종료와 함께 리셋되는 스택과 메모리와 달리, 저장소는 장기간 데이터를 보존한다.
코드는 또한 받은 메시지의 값, 전송자와 데이터와 함께 블록 헤더 데이터도 접근할 수 있으며, 코드는 또한 아웃풋으로 데이터의 바이트 어레이를 반환할 수 있다.
EVM 코드의 정식 코드 실행 모델은 놀랍게도 단순하다. 이더리움 가상 머신이 구동 중인 동안, 모든 연산 상태는 튜플 (block_state, transaction, message, code, memory, stack, pc, gas)
로 정의된다. 여기서 block_state
는 모든 어카운트를 포함하는 글로벌 상태로 이더 잔고와 저장소를 포함한다. 코드 실행 라운드가 시작될 때마다 현재의 명령문은 code
의 pc
번째 바이트(또는 pc >= len(code)
일 때 0)를 읽어들여서 찾는다. 각 명령문은 튜플에 어떻게 영향을 미치는지 개별 정의되어 있다. 예를 들면,
ADD
- 스택에서 2개의 아이템을 pop시켜, 아이템 총합 값을 push한다.
- 이 과정에서
gas
가 1 줄어들고pc
가 1 늘어난다.
SSTORE
- 스택에서 2개의 아이템을 pop시켜, 첫번째 아이템을 인덱스로 하는 저장소 특정 위치에 두번째 아이템을 삽입한다.
이더리움 가상 머신의 코드 실행을 JIT 컴파일(just-in-time compilation)을 통해 최적화하는 방법이 많지만, 기본적인 이더리움의 구현은 코드 수백 줄로 가능하다.
Blockchain and Mining
이더리움 블록체인은 여러 면에서 비트코인 블록체인과 유사하지만, 차이점도 존재한다. 주요한 차이점은 비트코인과 달리 이더리움 블록은 트랜잭션과 최신 상태 정보의 복사본을 갖도록 설계되었다는 점이다. 그 외에 블록 넘버와 난이도라는 두 값 또한 블록에 저장된다. 기본적인 이더리움 블록 유효성 검증은 다음과 같이 이루어진다:
- 참조된 직전 블록이 블록체인에 존재하는 유효 블록인지 확인
- 블록 타임스탬프가
i. 참조된 직전 블록 타임스탬프보다 나중인지
ii. 참조된 직전 블록 타임스탬프로부터 15분 이내인지 - 다음 저레벨 이더리움 컨셉 값의 유효성 검증
i. 블록 넘버 (block number)
ii. 난이도 (difficulty)
iii. 트랜잭션 루트 (transaction root)
iiii. 엉클 루트 (uncle root)
v. 가스 제한 (gas limit) - 블록에 대해 이루어진 작업증명의 유효성 검증
S[0]
에 대해 직전 블록 끝단에 저장된 상태값 할당TX
에 대해n
개의 블록 트랜잭션 리스트를 할당
*0 ... n-1
범위의 모든i
에 대해S[i+1] = APPLY(S[i], TX[i])
실행
If (어플리케이션이 오류를 반환) OR (현 시점까지 블록에서 소비된 총 가스량 >GASLIMIT
): return error;S_FINAL
에 대해S[n]
을 할당
*채굴자에게 블록 추가에 대한 보상 지불S_FINAL
상태값의 머클 트리 루트가 블록 헤더의 최종 상태 루트와 동일한지 확인
*동일한 경우: 유효 블록
*동일하지 않은 경우: 유효하지 않은 블록
이러한 프로세스는 언뜻 보면 매우 비효율적으로 보인다. 각 블록과 함께 모든 상태값을 저장해야 하기 때문이다. 그러나 실제로는 비트코인과 크게 차이나지 않는 퍼포먼스를 보인다. 상태값은 트리 구조에 저장되는데, 모든 블록의 트리는 직전 블록 트리로부터 매우 작은 부분만 변경되기 때문이다. 따라서 일반적으로 이웃한 두 블록은 트리 대부분이 동일할 것이며, 한 번 저장된 데이터는 이후 포인터(ie. 서브트리 해시)를 통해 재참조될 수 있다. "패트리샤 트리(patricia tree)"라고 불리는 특수한 트리는 머클 트리 개념을 수정하여 노드의 변경 뿐 아니라 효율적으로 삽입과 삭제를 허용하여 이러한 작업을 가능하게 만든다. 게다가 모든 상태 정보는 블록의 끝단에 위치하기 때문에, 모든 블록체인 히스토리를 저장할 필요가 없어진다. 이 점은 블록체인의 경우에도 해당하는데, 대략 5-20배의 공간 절감 효과가 난다.
물리 하드웨어 측면에서 컨트랙트 코드는 "어디서" 실행되는지 궁금해 하는 사람이 많다. 답은 간단하다. 컨트랙트 코드의 실행 프로세스는 상태 변환 함수의 일부이며, 다시 상태 변환 함수는 블록 유효성 검증 알고리즘의 일부이다. 따라서 트랜잭션이 블록 B
에 추가되면, 그 트랜잭션으로 시작되는 코드 실행은 언제나 블록 B
를 다운로드받아 유효성을 검사하는 모든 노드에 의해 이루어진다.
Coins mentioned in post:
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit