코어 이더리움 프로그래밍#2(Chapter 02) - NomadJin/Blockchain-Study GitHub Wiki
Chapter 02. 이더리움 플랫폼의 작동 원리
2.1 이더리움 플랫폼 살펴보기
컴퓨팅 분양에서 플랫폼은 일련의 소프트웨어 프로그램을 작동되게 하는 프레임워크를 말한다. 이더리움은 블록체인 내부의 이더리움 가상 머신 운영체제와 스마트 컨트랙트 개발언어, 이를 작동하고 관리하기 위한 다양한 서비스를 제공한다.
2.1.1 이더리움 작동 과정
이더리움 커뮤니티의 정의
이더리움은 정확히 프로그래밍한 대로 작동하는 스마트 컨트랙트를 작동시키는 분산된 플랫폼이다.
이더리움 지갑 설치와 사용
- 일반 사용자는 암호화폐 이더를 사용하기 위해 미스트(Mist)나 이더리움 월릿(Ethereum Wallet)을 설치한다.
- 메인 어카운트를 이더베이스라고도 하는데, 그 이유는 마이닝 등의 작업 대가를 이더로 지급 받을 때 기본 어카운트로 사용되기 때문.
모든 거래 기록의 공유 및 블록체인 구성
- 이더 금액의 이동은 특정 시점에 특정 사용자의 어카운트 상태를 다른 상태로 전이 => 트랜잭션
- 이 트랜잭션을이 모여 하나의 블록이 만들어지고, 이 블록이 시간순으로 연결되면서 일련의 블록체인이 된다.
- 정보 지연이나 미도달 사태 등의 문제가 없는지 확인하기 위한 방법으로 합의 알고리즘(PoW)
다양한 응용 앱 개발
- 이더리움의 스마트 컨트랙트를 이용하여 다양한 응용 서비스를 개발할 수 있음.
- 스마트 컨트랙트를 이용해서 개발한 일련의 서비스를 탈중앙화 앱인 DApp(Decentralized App, 댑)이라고 함.
2.2 이더리움 단일 상태 모델
2.2.1 이더리움 상태 전이 모델
트랜잭션이 발생할 때마다 이에 대한 신뢰 여부를 참여자들에게 묻고 과반수가 문제없다는 합의가 될 경우에만 해당 트랜잭션이 처리된다. 합법적으로 처리된 트랜잭션은 어카운트의 상태 변화를 가져온다.
이더리움에서의 거래, 상태 전이
이더리움의 모든 기본 단위는 어카운트(account)다. 중복되지 않는 식별자로 특정 주소를 부여 받고, 잔액이나 트랜잭션, 스마트 컨트랙트의 컴파일된 중간 코드처럼 필요한 데이터를 저장하기 위한 일련의 저장 공간들을 갖는다. 이러한 모든 것을 어카운트의 상태(state)라 한다.
- 모든 어카운트의 상태 정보는 블록과 블록 내에 연결된 머클 패트리시아 트리로 저장되고 관리된다.
- 상태 변이 함수 : 송금과 같은 트랜잭션이 될 수도 있고, 이더리움 가상 머신에서 실행되는 스마트 컨트랙트가 될 수도 있다.
- 어카운트의 특정 시점의 한 상태는 상태 변이 함수를 통해 단 하나의 상태(single state)로만 전이된다.
2.2.2 이더리움 플랫폼 참조 모델
이더리움은 데이터 계층, 합의 계층, 실행 계층, 응용 계층, 공통 계층으로 모델링할 수 있다.
데이터 계층
- 각종 데이터 구조를 정의하고 관련 데이터를 관리, 어카운트, 트랜잭션, 메시지와 리시트, 블록, 블록체인 등.
- ethdb 패키지, 구글이 만든 키/값 데이터베이스인 레벨DB(LevelDB)에 바이너리 형태로 저장
합의 계층
- 블록의 유효성을 검증하는 합의 엔진과 이 과정을 수행하는 마이닝, 마이닝 난이도, 가스(Gas), 이더 등의 처리
실행 계층
- 스마트 컨트랙트와 EVM의 처리.
공통 계층
- 공통 기능 : P2P 네트워크 프로토콜, 암호 해시, 전자 서명, 각종 인코딩 등
응용 계층
- 분산 파일 시스템 스웜(Swarm)
- 분산 메시징 시스템 휘스퍼(Whisper)
2.3 이더리움 플랫폼 구성
2.3.1 데이터 계층
*어카운트
- 외부 소유 어카운트(EOA, Externally Owned Account)
- 일반적으로 말하는 이러디움 사용자 어카운트
- 개인 키(Private key)로 관리하며, 스마트 컨트랙트 실행 코드를 가지고 있지 않다.
- 개인 키를 사용하여 전자 서명된 트랜잭션을 생성하고 실행함으로써 다른 EOA나 컨트랙트 어카운트에 메시지를 보낼 수 있다.
- 컨트랙트 어카운트(CA, Control Account)
- 스마트 컨트랙트의 정식 용어가 바로 컨트랙트 어카운트다.
- 오직 EOA나 다른 컨트랙트 코드에 의해서만 작동하고, 자신이 직접 새로운 트랜잭션을 실행할 수는 없다.
어카운트 정보
//패키지 : core/state, 파일명 : state_object.go
type Account struct {
Nonce uint64 // 해당 어카운트로부터 보내진 트랜잭션 수, 0으로 시작
Balance *big.Int // 이더 자고
Root common.Hash // 머클 매트리시아 트리의 루트 해시(Keccak256 암호 해시)
CodeHash []byte // 스마트 컨트랙트 바이트 코드 해시
}
어카운트 생성
- 모든 이더리움 어카운트는 개인 키와 공개 키의 쌍으로 정의되며, 이 둘은 비대칭 키라고 한다.
- 이더리움은 비대칭 암호호 알고리즘으로 256비트 ECDSA(타원형 곡선 방식)를 사용.
어카운트 상태
// 패키지 : core/state, 파일명 : state_object.go
type StateObject struct {
address common.Address // 어드레스
addrHash common.Hash // 어카운트 주소의 Keccak256 해시
data Account // 이더리움 어카운트
db *StateDB // 상태를 저장할 DBMS에 대한 포인터
// 쓰기용 캐시, 상태 값으로 필요한 데이터의 임시 저장 캐시
trie Trie // Tire 저장수
code Code // 컨트랙트의 바이트 코드
...
}
트랜잭션
다른 어카운트나 컨트랙트에 보낼 데이터 구조체(struct)로서, 전자 서명으로 암호화 한다. 이더를 전송하거나 스마트 컨트랙트의 특정 함수를 호출할 때 트랜잭션이 사용된다. 또한 스마트 컨트랙트를 이더리움 노드에 배포할 때도 사용.
리시트
모든 트랜잭션의 로그를 리시트에 저장한다.
블록체인 - 블록
어카운트에서 상태 전이를 유발하는 트랜잭션의 모든 관련 정보는 블록이라는 구조체에 저장된다. 이 블록들은 시간순으로 마치 체인처럼 서로 연결되어 있다. 이들 블록체인 P2P 네트워크와 동기화 트로토콜을 통해 모든 노드에게 전파되어 공유된다. 결국 블록체인이 공유 원장이다.
이더리움 블록은 블록 헤더와 엉클 블록, 그리고 트랜잭션으로 구성된다. 또한 마이닝 작업의 난이도 등도 포함하고 있다.
// 패키지 : core/types, 파일명 : block.go
type Block struct {
header *Header
uncles []*Header
transactions Transactions
...
td *big.Int // 난이도 총합(total difficulty)
}
블룸 필터 : 트랜잭션 목록이나 해당 트랜잭션들에서 생성된 로그들이 중복 저장되는 것을 막고, 이들 정보를 쉽게 찾기 위해 사용하는 것이 블록의 256비트 블룸 필터(Bloom Filter)다. 이렇게 하면 로그를 직접 블록 내에 저장하지 않게 되므로 저장 공간을 절약할 수 있다.
제네시스 블록
블록체인에서 첫 번째에 위치한 최초 블록을 말한다. 연결된 이전 블록이 없기 때문에 지난 블록을 참조할 수 없으며, 블록 넘버 또한 0이다. 또한, 어떤 트랜잭션도 포함하고 있지 않다.
type Genesis struct {
Config *params.ChainConfig
Nonce uint64 // 블록 생성을 위한 마이닝 작업 시 사용되는 64비트 해시
Timestamp uint64 // 해당 블록의 생성 시간
ExtraData []byte // 옵셩 항목, 최대 32비트 공간
GasLimit uint64 // 현재 블록체인에서 블록당 가스 지출의 최댓값
Difficulty *big.Int // 넌스값을 찾기 위한 마이닝 계산 시의 목표값이다.
Mixhash common.Hash // 넌스와 함께 마이닝 작업 시 블록 생성에 필요한 256비트 해시
Coinbase common.Address // 마이닝 작업 수행 후 보상과 트랜잭션 수행 대가를 전송할 어카운트 주소
Alloc GenesisAlloc // 일정 양의 이더를 특정 어카운트에 미리 할당할 수 있다.
// These fields are used for consensus tests. Please don't use them
// in actual genesis blocks.
Number uint64 `json:"number"`
GasUsed uint64 `json:"gasUsed"`
ParentHash common.Hash // 부모 블록 헤더의 Keccak256 암호 해시값. 제네시스 블록은 값이 0
}
이더리움 네트워크에 연결된 이더리움 클라이언트는 같은 제네시스 블록을 갖고 있을때만 서로를 연결하고 블록들을 싱크한다.
엉클 블록
블록 생성에 성공하였고 검증에 오류가 없어서 이더리움 네트워크를 통해 다른 노드에 브로드 캐스팅 되었으나 다른 마이너가 생성한 다른 블록에 비해 난이도가 낮아 블록체인에는 등록되지 못한 블록을 말한다. 비트코인에서는 스테일 블록 혹은 고아 블록이라고 한다.
엉클 블록이 많아지면 여러 문제를 일으킨다.
- 트랜잭션 처리 지연 : 두 명의 마이너가 동시에 채굴 시 정상 블록에 포함된 트랜잭션은 처리가 되는 반면, 엉클 블록 내에 트랜잭션은 즉시 처리되지 않는다.
- 컴퓨팅 파워의 낭비 문제 : 엉클 블록의 짧은 체인의 생성 과정은 불필요한 해시 계산을 위한 컴퓨팅 파워를 쓴셈이 된다.
- 보안 문제 : 엉클 블록 생성 후 다음 블록을 생성하면 평균 블록 생성 시간이 더 길어지기 때문에 블록 생성 후 난이도가 줄어들게 된다. 난이도가 줄어들면 블록 타임이 줄어들게 되고 컴퓨팅 파워가 큰 마이너의 영향력이 커지는 문제가 발생한다.
고스트 프로토콜
이더리움은 엉클 블록 문제를 고스트(GHOST, Greedy Heaviest Observed Subtree) 알고리즘 사용하여 해결한다. 블록 생성 시 정상 블록에 최대 2개의 어을 블록까지 추가하고 보상하여 엉클 블록의 문제를 해결한다.
블록체인
이더리움은 블록들을 시간순으로 서로 연결하여 블록체인을 구성한다. 앞 블록의 내용이 변경되면 뒤에 따라오는 모든 블록도 변경해야 하기 때문에 조작이나 위변조가 불가능한 불가역적 특성을 갖는다. 또한, 모든 트랜잭션에는 발생한 시간이 명시되기 때문에 해당 시점에 거래 사실을 명백히 확인할 수 있어서 거래 사실을 부인하지 못한다.
type BlockChain struct {
chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning
db ethdb.Database // Low level persistent database to store final content in
triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
gcproc time.Duration // Accumulates canonical block processing for trie dumping
hc *HeaderChain
rmLogsFeed event.Feed
chainFeed event.Feed
chainSideFeed event.Feed
chainHeadFeed event.Feed
logsFeed event.Feed
scope event.SubscriptionScope
genesisBlock *types.Block
mu sync.RWMutex // global mutex for locking chain operations
chainmu sync.RWMutex // blockchain insertion lock
procmu sync.RWMutex // block processor lock
checkpoint int // checkpoint counts towards the new checkpoint
currentBlock atomic.Value // Current head of the block chain
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
stateCache state.Database // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
blockCache *lru.Cache // Cache for the most recent entire blocks
futureBlocks *lru.Cache // future blocks are blocks added for later processing
quit chan struct{} // blockchain quit channel
running int32 // running must be called atomically
// procInterrupt must be atomically called
procInterrupt int32 // interrupt signaler for block processing
wg sync.WaitGroup // chain processing wait group for shutting down
engine consensus.Engine
processor Processor // block processor interface
validator Validator // block and state validator interface
vmConfig vm.Config
badBlocks *lru.Cache // Bad block cache
}
블록체인 동기화와 라이트 체인
- 전체 동기화(Full Sync) : 전제 블록을 동기화. 제네시스 블록으로부터 블록 헤더들과 바디 등 모든 블록체인을 동기화하고 모든 항목의 유효성을 검증한다.
- 빠른 동기화(Fast Sync) : 최근의 상태, 트랜잭션, 리시트 등을 포함하고 있는 블록 헤더만을 동기화한다.
- 경량 동기화(Light Sync) : 현재 상태 정보만 동기화. 특정 세부 항목들의 검증이 필요할 경우 해당 세부 항목 값을 포함하고 있는 전체 정보를 다운로드하여 처리.
LES(Light Ethereum Subprotocol) 는 블록의 헤더만 있고 추가 상세 정보는 필요할 때 다운로드할 수 있는 프로토콜이다.
머클 패트리시아 트리
블록체인의 크기가 계속해서 증가한다면 공유를 위해 많은 데이터를 동기화해야 하기 때문에 이더리움에서는 이를 개선하기 위해 머클 패트리시아 트리(Merkle Patricia Tree)라는 암호 해시 기반의 트리 자료구조를 사용한다. 트리 내의 모든 정보는 레벨DB(LevelDB)에 저장한다.
해시 함수
임의의 크기 값을 입력했을 때 고정 크기 값을 생성해 해는 함수다. SHA256() 바이너리 해시 함수는 서로 다른 크기의 문자열을 받은 후 64바이트 크기의 고정된 암호화된 문자열 값을 생성해 낸다.
머클 트리
암호화 해시 함수로 일련의 데이터를 해싱한 후, 이 결과값을 다시 암호화 해시하여 상위 노드로 만들어 트리 구조를 생성한다. 트리 구조에서 가장 상위의 노드를 머클 루트라고 부른다. 암호화 해시 함수를 이용하여 만들기 때문에 데이터의 변조를 막을 수 있고, 특정 자식 노드 중 하나의 해시값을 알면 그 노드의 모든 자식 노드의 데이터를 검증할 수 있다.
이더리움은 머클 트리를 사용하여 전체 어카운트 정보를 담고 있는 상태(ROot)와 트랜잭션(Transaction), 그리고 트랜잭션의 처리 결과를 알 수 있는 리시트(ReceiptHash)의 값을 저장하고, 각 머클 루트를 Keccak256으로 암호 해싱한 후 이를 해당 블록 헤더에 포함한다.
이렇게 블록 헤더에 상태, 트랜잭션, 리시트 머클 트리가 포함되어 있기 때문에 블록 헤더만 다운로드해도 다양한 블록체인의 상태를 조회할 수 있다.
머클 패트리시아 트리
이더리움 블록 헤더에 포함된 트랜잭션과 리시트는 블록 내 저장되면 변하지 않는다. 그러나 어카운트 정보를 포함하고 있는 상태는 키와 값의 맵 구조이기 때문에 자주 변경된다(어카운트 생성 또는 삭제 잔액 변경 등). 상태 트리는 머클 패트리시아 트리를 사용한다.
이더리움 팀은 두 가지 추가적인 개선사항을 도출함.
- 트리의 깊이를 한정 : 트리 깊이를 한정 짓지 않으면 디도스 공격으로 인한 성능 저하를 일으킴
- 업데이트가 되더라도 머클 루트가 변경되지 않도록 머클 루트에 숫자 값을 주고 이 값에 한정
머클 패트리시아 주요 특징
- 머클 패트리시아 트리 내의 모든 항목은 RLP(Recursive Length Prefix) 인코딩 된다.
- 공백 노드 : 비어 있는 노드(Null)
- 리프 노드 : 일련의 RLP 인코딩된 경로, 값. 값은 이더 같은 실제 값을 말한다.
- 확장 노드 : 일련의 RLP 인코딘된 경로, 키. 키는 연결된 노드의 해시값이고 레벨DB 호출 시 키 값으로 사용하다.
- 브랜치 노드 : 17개 항목으로 구성된 리스트 구조.
머클 상태 전이 증명
블록 헤더에는 상태 정보 트리, 트랜잭션 트리, 리시트 트리의 암호화된 해시 루트를 포함함으로써 다양한 정보를 조회할 수 있다.
http://hamait.tistory.com/959 머클 패트리시아 트리를 이해하기 위한 여정
이더(Ether)
이더리움에서 사용하는 암호화폐. 다른 사용자에게 전달할 수도 있고 잔달받을 수도 있다.
가스(Gas)
이더리움 시스템의 운영 토큰. 스마트 컨트랙트, 트랜잭션이 구동되면 이더리움 가상머신을 이용하고 블록 생성 등을 위해 마이닝을 하는 등 일련의 이더리움 상의 리소스를 이용한다. 이더리움 리소스를 이용하려는 사람은 이에 대한 대가를 지급하야. 가스는 운영 토큰으로서 이더리움 플랫폼을 활용한 대가를 지급하는 데 사용된다.
가스를 사용하는 또 다른 이유는 트랜잭션 남용을 막기 위함.
- 최대 트랜잭션 실행 비용 : 가스 총량 X 가스 가격
- 가스 가격 : 가스당 트랜잭션을 요청한 사람이 지급할 가격
- 가스 총량 : 트랜잭션 수행에 소비될 총 가스량에 대한 추정치
가스 가격이 높을수록 해당 트랜잭션이 빨리 처리된다.
참고로, 블록 가스 총량이라는 개념이 있는데 하나의 블록에 최대 6,700,000 가스를 수용할 수 있고, 1개 트랜잭션 처리 시 최소 가스 비용은 21,000 가스다. 따라서 산술적으로 1개의 블록에는 약 319개의 트랜잭션을 포함. 현재 평균 100개 정도. 블록 생성 시간이 16~18초 가량인데, 블록 가스 총량을 최대한 늘리면 태랜잭션의 처리량과 속도를 높이는 효과를 얻을 수 있다.
전자 서명
트랜잭션 처리의 안정성을 담보하기 위해 이더리움은 전자 서명 암호화를 사용한다.
일반적으로 암호화에서 사용하는 키는 **비밀 키(Secret Key)**와 **공개 키(Public Key)**로 구분된다.
- 비밀 키 암호화 : 암호화와 복호화 시에 하나의 공통 비밀 키만 사용한다. 대칭 키 암호화라고도 한다. 문제점은 하나의 비밀 키를 안전한 방법으로 공유하기 어렵다는 것
- 공개 키 암호화 : 암호화 복호화 시에 서로 다른 2개의 키를 사용한다. 개인 키와 공개 캐 2개를 사용. 개인 키는 주인만 알고 공개 키는 모든 사람에게 공개하는 키를 말한다.
암호화를 할 때 공개 키를 사용하는 경우를 공개 키 암호화 방식, 개인 키로 암호화를 하는 경우를 전자 서명.
- 공개 키 암호화 : 공개 키를 이용해 암호화. 해당 공개 키의 쌍을 이루는 개인 키를 가지고 있는 사람만이 복호화를 할 수 있다.
- 전자 서명 암호화 : 개인 키를 이용해 암호화. 해당 개인 키에 매칭되는 공개 키를 가지고 있는 사람은 모두 해당 암호화된 메시지를 복호화할 수 있다.(공인인증서 방식)
이더리움에서는 전자 서명 암호화 방식을 사용하며, 비대칭 키 생성 및 암호화 알고리즘으로 256비트 ECDSA를 사용한다.
트랜잭션 처리
- 트랜잭션을 개인 키로 ECDSA 전자 서명 암호화
- 이더리움 클라이언트는 해당 트랜잭션을 마이너를 포함한 모든 노드에 보르드 캐스트.
- 마이너는 트랜잭션을 전달받은 후 해당 트랜잭션이 문법에 맞는지, 전자 서명은 유효한지, 사용자의 어카운트에 있는 넌스와 맞는지 등의 트랜잭션 유효성을 검증. 가스 비용 계산. 수신처 아카운트 주소 확인. 모든 검증 작업이 완료되면 최종 실행을 위해 트랜잭션 풀에 해당 트랜잭션을 등록.
- 마이너는 트랜잭션 풀에서 트랜잭션 처리 시 가스 실행 비용이 높은 순으로 트랜잭션을 선택. 어카운트 간의 전송. 컨트랙트라면 컨트랙트 코드를 작동함.
2.3.2 합의 계층
이더리움은 중앙의 특정 기구나 서버에서 새롭게 생성된 블록과 트랜잭션의 유효 여부를 판단해 주지 않느다. 서로 모르는 대상에 정보를 주고 받을 때 해당 정보가 정확한지를 위해서는 참여자들간의 합의가 절대적으로 필요하다.
합의 알고리즘
탈 중앙집중화된 P2P 네트워크에서는 다양한 원인으로 인해 정보 전달 과정에서 문제가 발생할 수 있어 전달된 정보가 문제가 없다는 것을 참여자들이 스스로 검증하고 그 유효성을 판단해야 한다. 이를 위한 방법이 합의 수단이다.
작업 증명 방식
작업 증명(Proof of Work)은 복잡한 계산 문제의 해답을 가장 빨리 찾은 마이너의 블록을 블록체인에 등록하고 이에 대한 수행 결과로 보상을 한다. 트랜잭션을 승인하고 블록을 생성할 때 어려운 계산 문제를 수행하게 함으로써 악의적인 블록을 위변조하고 블록체인을 임의로 조작하는 것을 막을 수 있다
블록 전파
목표값을 찾아 블록을 생성한 마이너들은 해당 블록 주변 노드에 브로드캐스팅한다.
체인의 분기 : 하드 포크와 소프트 포크
포크(Fork)란 블록체인 시스템의 업그레이드이다.
- 하드 포크 : 모든 마이너와 사용자가 반드시 업그레이드해야 하는 경우를 말함
- 소프트 포크 : 이전 버전과 신규 버전의 블록체인이 호환되기 때문에 사용자는 반드시 업그레이드할 필요는 없지만 마이너들은 업그레이드해야 하는 경우. 정확히 말하면, 마이너의 마이닝 계산 능력의 50% 이상이 업그레이드해야 새로운 블록체인 구조로 변경되고 유효성을 유지할 수 있음.
이더리움 합의 엔진
강력한 해시 연산에 특화된 ASIC(Application-Specific Integrated Circuit) 칩이 개발. 이더리움 합의 엔진인 이대시(Ethash)는 기본적으로 해시 계산에 특화된 ASIC 칩의 용도를 무력화한 후 해시 계산력이 중앙에 집중되는 것을 막고 경량 클라이이언트에서도 블록을 검증할 수 있는 것을 목표로 개발
합의 엔진, 이대시
이대시(Ethash)는 메모리 기반의 이더리움 PoW 합의 엔진이다. GPU나 CPU 병렬 계산에 최적화되어 메모리가 필요없는 비트코인과는 달리, ASIC 칩을 이용한 마이닝을 지양하기 위해 메모리를 쓰는 방식을 채택하였다. 일반적으로 메모리를 읽고 복제하는 속도가 해시 계산보다 느려서 해시 계산력을 높이는 것이 큰 의미가 없다.
DAG
시드 해시로 생성된 약 2G 정도의 캐시 데이터 집합을 DAG(Directed Acyclic Graph) 파일이라고 한다.
난이도와 타임스탬프
블록 생성을 위한 목표 난이도(Target Threshold)를 도출하기 위해서는 현재 시각의 타임스탬프가 필요하다. 현재 타임스탬프틑 모든 블록의 블록 헤더에 포함되어 블록의 유효성을 검사하는데 사용된다. 만약 마이너가 악의적으로 높은 타임스탬프 값을 사용한다면 난이도가 낮아지기 때문에 블록 생성 시 태책이 안될 확률이 높아지고, 일부러 낮은 타임스탬프 값을 사용할 경우에는 난이도가 높아지기 때문에 마이닝 비용이 상승한다.
블록 타임
블록 타임(Block Time)은 블록이 생성되고 네트워크에 전파되는데 걸리는 시간을 말한다. 한 개의 블록이 생성되고 전파되는 데 12초가 가장 적합하다고 판단하고 잇으며, 최종 12초의 블록 생성 시간을 만드는 것을 목표로 하고 있다.
난이도를 낮추면 평균 블록 생성 시간이 짧아지고, 난이도를 높이면 평균 블록 생성 시간이 길어진다.
2.3.3 실행 계층
스마트 컨트랙트
블록 헤더의 데이터뿐만 아니라 특정 값이나 발신자 및 수신되는 메시지의 데이터를 조작하는 등 이더리움의 상태 변화와 데이터 저장 등이 가능한 코드다. 이더리움은 단순 암호화폐 개발 플랫폼을 벗어나 보다 다양한 분야에 적용할 수 있는 블록체인 컴퓨팅 플랫폼으로 발전하게 되었다.
모든 트랜잭션을 실행할 때는 해당 실행 비용을 지급해야 한다. (악의적인 코드를 막고 데이터의 무결성을 지키기)
컨트랙트의 모든 내용과 입력을 공유한다. (악의적인 컨트랙트 조작을 방지)
컨트랙트 메시지
컨트랙트 간의 호출은 메시지라는 특별한 구조체를 사용하여 호출된다. 메시지는 외부 어카운트가 아니라 캔트랙트 어카운트에 의해서만 생성되며, 함수 호출 시 다른 컨트랙트에게 전달된다. 메시지는 트랜잭션과 다르게 가상 객체이기 때문에 별도 저장할 필요가 없으며, EVM 실행 환경 내에서만 존재한다. 메시지는 가스 비용이 발생하지 않는 컨트랙트 간의 내부 호출.
스마트 컨트랙트 작동 과정
이더리움 가상 머신
EVM(Ethereum Virtual Machine)은 이더리움 스마트 컨트랙트의 바이트 코드를 실행하는 32바이트 스택 기반의 실행 환경으로 스택의 최대 크기는 1024바이트이다. 이더리움의 각 노드는 EVM을 포함하고 있으며, EVM을 통해 컨트랙트의 바이트 코드를 Op코드로 변환 후 내부에서 실행한다.
솔리디티 같은 상위 수준 언어로 개발된 스마트 컨트랙트를 컴파일한 후 생성되는 컨트랙트 바이트 코드는 EVM에서 Op코드로 치환되어 실행된다.
EVM의 주요 특징
- 임시 저장소와 영구 저장소를 구분하여 임시 저장소에 저장된 값은 해당 인스턴스 안에서만 유효하고, 영구 저장소에 저장된 값은 해당 컨트랙트 전체에 유효하다.
- EVM에서 바이트 코드를 실행기 위한 세 가지 요소
- LIFO(Last-In-First-Out) 컨테이너에 값을 푸시하거나 팝하기 위한 스택
- 무한대로 확장 가능한 바이트 배열을 담을 수 있는 메모리
- 계산이 끝나면 리세되는 스택이나 메모리와는 달리 영속적으로 값을 저장하기 위한 저장소
- 32바이트 워드 크기를 지원
- 다른 언어(자바, Lisp, Dialect, Lua)의 VM에 비해 이더리움의 요구사항이 훨씬 단순하기 때문에 자체 VM을 개발
- 메모리 크기가 가변적이고 스택의 크기에 제한이 없다
- 반복 호출 횟수(call depth)를 1024로 제한
2.3.4 공통 계층
공통 계층은 전체 계층에서 공통으로 사용하는 기능을 포함하고 있다. 먼저, 노드 탐색을 위한 노드 디스커버리 프로토콜과 RLPx P2P 네트워크 프로토콜과 어카운트 상태 정보와 트랜잭션 및 리시트 등 블록체인의 데이터를 효율적으로 저장하기 위한 스토리지 기능 등이 해당. 또한, Keccak256 암호 해시, ECDSA의 키 암호화와 RLP 데이터 인코딩 등.
이더리움 P2P 네트워크
이더리움은 완전 분산형 P2P 토폴로지로 구성된다. 이더리움 P2P 네트워크에 연결된 모든 노드는 같은 역할과 기능을 수행한다.
이더리움 기본 P2P 프로토콜, RLPx/devP2P
P2P 네트워크상에서 일반 전송과 애플리케이션 간의 통신을 위해 RLPx라는 암호화된 피어 간 네트워크 프로토콜을 사용한다. RLPx에는 피어 간 노드를 탐색하기 위한 기능과 타원형 곡선 방식(ECDSA)으로 서명된 UDP 프로토콜과 암호화된 TCP 프로토콜 등 이더리움 전반에 걸쳐 사용되는 P2P 네트워크 기능이 모두 포함되어 있다.
노드 디스커버리 프로토콜
이더리움 네트워크 일부가 되려면 네트워크상의 다른 노드들과 서로 연결되어야 한다. 중앙에 항시 연결될 서버가 없는 상황에서 어떻게 노드들이 다른 노드들을 찾고 연결할까? 이더리움은 분산 해시 테이블인 카데리마(Kadelima) 프로토콜을 기반으로 네트워크상에 노드를 탐지하는 노드 디스커버리 프로토콜을 개발하였다.
일반 노드가 이더리움 네트워크에 연결되면 다른 노드를 탐색하는데, 최초 탐색은 하나 이상의 부트스트랩 노드에 접속하여 연결된 노드의 목록을 받은 후 해당 노드들과 접속한다. 부트스트랩 노드는 블록체인 정보는 저장하지 않고 네트워크상의 피어 노드드들을 찾는데 사용되며, 일정 시간 동안 연결되어 있는 노드의 목록을 유지한다.
부트스트랩 노드
부트스트랩 노드 작동 순서
- 부트스트랩 노드는 항상 작동하며, 일정 시간 동안 접속했던 노드들의 목록을 유지하고 있음.
- 새로운 피어 노드가 이더리움 네트워크에 최초 접속할 때 부트스트랩 노드에 접속한 후 지난 일정 시간 내에 접속했던 피어들의 목록을 공유 받는다.
- 공유 받은 목록으로 피어 노드들에 연결한 후에 부트스트랩 노드와의 연결을 끊는다.
이더리움 데이터 저장
이더리움은 기본 저장소로 내부에 ethdb 패키지를 통해 [키와 값] 저장소인 레벨DB를 사용한다. 상태 트리를 비롯하여 트랜잭션 머클 트리와 리시트 머클 트리, EVM의 비휘발성 저장소 등 이더리움에서 스토리지에 저장될 필요가 있는 모든 정보가 레벨DB에 저장된다.
레벨DB
관계형 데이터베이스가 아니기 때문에 SQL을 지원하지 않고 [키와 값] 형태로 데이터를 저장하고 조회 및 갱신, 커서 등 다양한 조작을 위한 함수를 제공한다. 구글의 데이터 압춥 알고리즘인 스내피(Snappy)를 사용하여 저장 공간을 효율적으로 사용한다.
이더리움 데이터베이스(ethdb)
이더리움은 ethdb라는 패키지로 레벨DB를 래핑하여 사용하고 있다. 나중에 코드의 변경 없이도 레벨DB가 아닌 다른 DBMS로 손쉽게 교체할 수 있다.
비록 레벨DB가 사용하기 쉽다는 장점은 있으나 데이터의 조회 기능이 너무 단순하고 인덱스를 지원하지 않는다. 따라서 키를 파일에 순차적으로 저장한 후 해당 키에 해당하는 값을 찾을 때는 디스크에 저장된 많은 파일을 모두 탐색해야하는 문제점 있음. 이 문제를 해결하기 위해 MongoDB 등으로 변환하는 프로젝트 등이 활발하게 추진되고 있다.
2.3.5 응용 계층
이더리움은 스마트 컨트랙트에 기반을 둔 탈중앙화 응용 앱인 DApp(Decentralized App)을 지원한다. (https://www.stateofthedapps.com/)
DApp 외에도 P2P 파일 시스템인 스웜(Swarm)과 P2P 메시징 서비스인 휘스퍼(Whisper)를 함께 제공하여 이더리움 기반의 다양한 응용 서비스의 개발을 지원하고 있다.
DApp
DApp(Decentralized App)은 스마트 컨트랙트 기반의 웹 서비스다. 스마트 컨트랙트를 개발한 후 블록체인에 배포하면 스마트 컨트랙트의 어카운트 주소와 ABI(Application Binary Interface) 등이 생성된다. ABI는 스마트 컨트랙트의 바이트 코드를 일반 프로그램에서 호출하고 실행시킬 수 있는 정보와 인터페이스를 제공한다.
DApp의 구동 환경
이더리움은 web3.js라는 자바스크립트 라이브러리를 제공한다. 개발자는 스마트 컨트랙트 주소와 ABI를 알면 web3.js 라이브러리를 통해 스마트 컨트랙트를 생성하고 특정 함수를 실행시킬 수 있다.
차세대 분산 웹으로서의 DApp
중앙 서버에 집중된 서비스를 중앙집중형 서비스 플랫폼 모델이라고 한다. 이에 반해, 이더리움 기반의 DApp 서비스는 중앙의 웹 서비스와 데이터를 집중하지 않고 완전 탈중앙화된 블록체인을 이용하여 서비스를 제공한다. 분산된 로직을 수행하는 스마트 컨트랙트와 분산 파일 시스템인 스웜, 분산 메시징 시스템인 휘스퍼를 이용하여 탈중앙화된 서비스를 구축.
P2P 메시징 시스템, 휘스퍼
기존의 메시징 서비스는 네트워크 종단간 연결을 암호화하고 주고 받는 메시지를 암호화 하지만 근본적으로 중아의 메시징 서버에 메시지가 저장되기 때문에 완벽하게 보안 문제를 해결할 수 없다. 휘스퍼는 이더리움의 P2P 네트워크를 기반으로 한 추적 불가능한 P2P 메시징 서비스다. 중앙 서버에 메시지를 저장하지 않고 종단간 견결과 메시지 모두 암호화되기 때문에 추적이 불가능하고 안전하다.
P2P 파일 시스템, 스웜
스웜은 인센티브 방식으로 운영되는 P2P 파일 시스템이자 기존 CDN(Content Delivery Network)과 유사한 콘텐츠 전달 채널이다. 스웜은 블록체인의 데이터뿐만 아니라 DApp의 코드와 데이터, 콘텐츠 같은 이더리움의 공용 데이터를 탈중앙화된 분산 형태로 저장하고 해당 콘텐츠를 전달하기 위해 사용된다.
스웜은 특정 서버 주소가 없이 업로드 다운로드 된다. 사용자가 스웜에 파일을 업로드하면 실제 해당 파일은 온란인 상태에 있는 스웜 노드 중 하나에 업로드 된다. 업로드된 해당 노드가 오프라인 상태라도 해당 파일은 이미 다른 스웜 노드들 간에 동기화되어 있다. 스웜에서는 데이터 삭제 기능은 없다.