본문으로 바로가기

이전 블록체인의 기술적 개념에 대한 포스팅에서 설명한것처럼 이더리움에서도 트랜잭션과 상태정보는 블록에 포함이 되고, 이러한 블록들이 연결된것을 블록체인이라고 합니다. 이들 블록체인이 P2P 네트워크를 통해 연결된 모든 노드에 전파되어 공유됩니다. 


블록

이더리움에서 블록은 블록헤더엉클블록, 트랜잭션으로 구성됩니다.


먼저 블록에 어떤 정보가 포함되는지 살펴보도록 하겠습니다.  - https://etherscan.io/block/5529578

하나의 블록은 하기와같은 정보를 포함하고 있습니다.



블록은 다음과 같은 구조로 정의되어 있습니다.  ( core/types/block.go )

하기와 같이 블록은 블록헤더, 엉클블록, 트랜잭션으로 구성되어 있습니다.


// Block represents an entire block in the Ethereum blockchain.
type Block struct {
header *Header
uncles []*Header
transactions Transactions
// caches
hash atomic.Value
size atomic.Value
// Td is used by package core to store the total difficulty
// of the chain up to and including the block.
td *big.Int
// These fields are used by package eth to track
// inter-peer block relay.
ReceivedAt time.Time
ReceivedFrom interface{}
}


그리고 블록헤더는 다음과 같이 구성되어 있습니다.


// Header represents a block header in the Ethereum blockchain.
type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Time *big.Int `json:"timestamp" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
Nonce BlockNonce `json:"nonce" gencodec:"required"`
}


ParentHash : 부모 블록의 해쉬값

UncleHash : 현재 블록의 엉클블록들의 해쉬값

Coinbase : 마이닝후 해당 트랜잭션의 수수료를 받을 어카운트 주소

Root : 어카운트의 상태정보가 모여있는 머클 패트리시아 트리의 루트 노드 해쉬값

TxHash : 블록의 모든 트랜잭션에 대한 머클트리의 루트노드 해쉬값

ReceiptHash : 블록내 모든 트랜잭션에 대한 리시트들의 머클트리의 루트노드 해쉬값

Bloom : 로그 정보를 사용하는데 사용하는 32바이트 블룸필터

Difficulty : 블록 난이도로 이전블록의 난이도와 타임스탬프로 계산

Number : 현재 블록번호

GasLimit : 블록당 지급가능한 최대 가스 총합

GasUsed : 블록내 트랜잭션에 사용된 가스의 총합

Time : 블록의 최초 생성시간

Extra : 블록의 기타정보

MixDigest, Nonce : 작업증명에서 해쉬값을 계산하는데 충분한 계산횟수를 보장하기 위해 사용하는 값


블록헤더를 도식화해보면 다음과 같습니다.

Root, TxHash, ReceiptHash는 머클트리의 각 노드를 hash한 최종 루트해쉬값에 해당하고 나머지 값들은 각각 하나의 상수로 볼수 있습니다.




제네시스 블록

제네시스 블록은 블록의 첫번째에 위치하는 최초 블록을 말하며 블록번호 0번에 해당되고 어떤 트랜잭션도 포함되지 않습니다.

제네시스 블록은 다음과 같이 정의되어 있습니다. ( core/genesis.go )


// Genesis specifies the header fields, state of a genesis block. It also defines hard
// fork switch-over blocks through the chain configuration.
type Genesis struct {
Config *params.ChainConfig `json:"config"`
Nonce uint64 `json:"nonce"`
Timestamp uint64 `json:"timestamp"`
ExtraData []byte `json:"extraData"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Mixhash common.Hash `json:"mixHash"`
Coinbase common.Address `json:"coinbase"`
Alloc GenesisAlloc `json:"alloc" gencodec:"required"`
// 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 `json:"parentHash"`
}


Alloc : 일정량의 이더를 초기에 어카운트에 할당할때 사용하는 값으로 마이닝작업이 없이 초기에 일정량의 이더를 발생하여 ICO를 통해 판매하는데 사용할수 있습니다.

Coinbase : 마이닝후 보상코인을 수령할 주소인데 제네시스 블록에서는 마이닝이 수행되지 않기 떄문에 어떠한 값을 할당해도 무방합니다.

Nonce : mixhash와 함께 마이닝시에 사용되는 값, 8바이트

Mixhash : Nonce와 함께 마이닝시에 사용되는 값, 32바이트

Difficulty : nonce 값을 찾기위한 마이닝시의 목표값

ExtraData : 32바이트 옵션항목

GasLimit : 블록의 최대 가스량

ParentHash : 이전 부모블록의 해쉬값, 제네시스블록은 부모가 없기떄문에 0으로 할당

TimeStamp : 블록의 생성시간


이더리움을 private network로 동작시킬때 초기 genesis 블록정보를 genesis.json 화일에 명시하고 지정하여 제네시스 블록을 생성할수 있습니다.


{
"config": {
"chainId": 101,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc" : {},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x400",
"extraData" : "",
"gasLimit" : "0x2fefd8",
"nonce" : "0x0000000000000042",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00"
}


그리고 alloc에 미리 어카운드정보와 이더발행량을 명시하여 초기에 이더를 발생할수도 있습니다.


"alloc": {
"0x0000000000000000000000000000000000000001": {"balance": "111111111"},
"0x0000000000000000000000000000000000000002": {"balance": "222222222"}
}


이렇게 정의한 genesis.json 화일을 사용하여 geth를 통해 private network로 시작할수 있습니다.

genesis.json 화일은 하기에 명시한 my_data_folder에 위치시켜야 합니다.


$ geth --datadir "my_data_folder" init genesis.json

 


엉클블록과 고스트 프로토콜

동시에 불록이 생성되어 전파되는경우 블록의 유효성 검증은 통과되었지만, 블록의 난이도가 상대적으로 낮아 블록으로 채택되지못한 블록을 엉클블록이라고 합니다. 엉클블록이 많아지만 다음과 같은 문제가 발생합니다.


1. 엉클블록에 포함된 트랜잭션은 처리가 되지않기 떄문에 트랜잭션처리의 지연문제가 발생

2. 엉클불록은 체인에 연결되지않기 떄문에 엉클블록을 발견하는데 사용된 컴퓨팅파워가 낭비되는 문제가 발생

3. 엉클블록이 생성된후 다음블록이 생성되면 평균블록 생성시간이 늘어나기 떄문에 블록생성 난이도가 낮아지기 떄문에 네트워크 보안수준을 떨어뜨리게 됩니다.


이더리움에서는 이러한 엉클블록의 문제를 고스트(Ghost : Greedy Heaviest Observed Subtree) 알고리즘을 사용하여 해결하고 있습니다.


블록체인 동기화와 라이트체인

이더리움은 현재 3가지 동기화 방식을 지원하고 있습니다.


1. 전체 동기화(Full sync) - 제네시스 블록부터 현재블록까지 모든블록을 동기화

2. 빠른 동기화(Fast sync) - 최근의 상태, 트랜잭션, 리시트 등을 포함하는 블록헤더만을 동기화. 이전 히스토리를 알수없기 떄문에 트랜잭션의 유효성 검증을 할수가 없음.

3. 경량 동기화(Light sync) - 현재 상태정보만 동기화


동기화방식은 geth 실행시에 지정을 할수 있습니다. 다음과 같이 선택가능한 sync mode는 "fast, full, light" 입니다.


// sync mode : fast, full, light

$ geth --syncmode "fast" 


이더와 가스

이더는 이더리움 시스템에서 사용되는 암호화폐이고 가스는 트랜잭션의 비용지불에 사용되는 운영토큰입니다. 이더가 거래소등에서 거래가 되며 변동성이 크기때문에, 변동성이 없는 가스를 만들어 트랜잭션 비용지불에 사용되며, 가스는 이더와 교환이 됩니다.


이더단위는 다음과 같이 나누어지며, 가장 기본단위는 wei가 되며, 10^18 wei가 1 Ether에 해당됩니다.


명칭

WEI 기준

Ether 기준

WEI10.000000000000000001
Ada10000.000000000000001
Fentoether10000.000000000000001
Kwei10000.000000000000001
Mwei10000000.000000000001
Babbage10000000.000000000001
Pictoether10000000.000000000001
Shannon10000000000.000000001
Gwei10000000000.000000001
Nano10000000000.000000001
Szabo10000000000000.000001
Micro10000000000000.000001
Microether10000000000000.000001
Finney10000000000000000.001
Milli10000000000000000.001
Milliether10000000000000000.001
Ether10000000000000000001
Einstein10000000000000000000001000
Kether10000000000000000000001000
Grand10000000000000000000001000
Mether10000000000000000000000001000000
Gether10000000000000000000000000001000000000
Tether10000000000000000000000000000001000000000000


가스는 이더리움 시스템의 운영토큰입니다. 이더리움에서 스마트컨트랙트의 실행이나 트랜잭션이 실행될떄 이더리움 시스템의 리소스를 사용하기 떄문에 이러한 사용에 대한 비용을 지불하는데 사용이 됩니다. 마이닝에 대한 수수료로 채굴자에게 지불되는데도 사용이 되며, 무분별한 트랜잭션이나 악의적인 스마트 컨트랙트의 실행을 방지하기 위한 목적으로도 사용이 됩니다.

트랜잭션에 사용되는 비용은 "가스 총량 * 가스 가격"으로 계산되며 이전 포스팅의 트랜잭션에 포함되어 있는 "GasLimit * Price" 로 계산됩니다.

트랜잭션에서의 이 비용이 높을수록 해당 트랜잭션은 채굴자에 의해 우선적으로 선택되기 떄문에 빨리 처리될수 있습니다.

블록가스 총량(Block Gas Limit)은 한 블록에 담을수 있는 가스의 총량을 의미합니다. 현재 한블록당 블록가스 총량은 6,700,000 가스이고, 하나의 트랜잭션당 최소 처리비용은 21,000 가스이기 떄문에 블록 하나당 최대 319개의 트랜잭션을 처리할수 있습니다. 실제 블록에는 평균적으로 100개정도의 트랜잭션과 스마트컨트랙트가 포함되고 있습니다.


다음은 메타마스크에서 이더의 전송에 사용되는 가스를 보여주고 있습니다.

일반적으로 트랜잭션에는 21000  가스의 GasLimit가 사용되며, GasPrice는 사용자가 선택을 할수 있으며, 위에서 언급한것처럼 이 값을 높일경우 채택될 확율이 높아집니다.



이러한 GasPrice 값을 변경해가며, 해당 트랜잭션이 수행되는데 얼마의  시간이 소요될지 확인을 해볼수 있습니다.

다음과 같이 GasLimit은 21000으로 고정된 상태에서 Gas price을 올려가며 해당 트랜잭션이 평균적으로 얼마의  시간후에 채굴가능한지 확인해볼수 있습니다.

하기의 트랜잭션은 Gas Price를 6 Gwei로 설정할경우 수수료는 0.00013 이더, 이는 0.086 달러에 해당되고, 평균적으로 0.9분후 해당 트랜잭션은 채굴될 가능성이 있음을 말합니다. 이와같이 Gas price를 변경해가며 트랜잭션의 채택시간을 확인해볼수 있습니다. 이와같은 방식으로 트랜잭션에는 변동성이 큰 이더가 사용되지않고 가스가 사용되어 트랜잭션에 고정비용이 사용되도록 구성되어 있습니다.


 [ETH Gas Station]


트랜잭션이 수행되며 가스가 사용되는 과정은 다음과 같습니다.

트랜잭션이 시작되면 GasLimit  값으로 가스값은 초기화가 되면서 GasLimit * Price 에 해당하는 이더가  사용자의 계좌에서 차감됩니다. 트랜잭션이 수행되면서 가스가  소모되고, 최종 트랜잭션이 수행된후에는 남은 가스는 다시 사용자에게 반환됩니다. 소모된 가스비용은 채굴자에게 수수료로 전달이 됩니다.


[그림참조 : How does Ethereum work, anyway?]


트랜잭션이 시작된후 완료되기 전에 가스를 다 사용하게 되면 진행중인 트랜잭션은 취소가 되고 revert 되어 트랜잭션 이전상태로 돌아가게 되지만 사용한 가스는 채굴자에게 보상으로 지급되기 떄문에 반환되지 않습니다.


[그림참조 : How does Ethereum work, anyway?]