Ethereum Basics - Block

블록체인과 이더리움을 이해하기 위한 기초 개념인 블록에 대해 알아봅니다.

블록체인의 기본 구성요소, Block

블록체인에서는 여러 개의 Transaction(유저가 발생시키는 상태의 변화)을 하나의 블록으로 묶어 저장합니다. Block은 Header와 body로 구성되며, 새로운 블록이 생성될 때 마다 이전 블록의 해시를 새롭게 생성된 Block의 Header에 저장함으로서 데이터의 무결성을 보장하는데 이러한 데이터 구조가 마치 체인으로 연결된 것 처럼 보여 블록체인이라는 이름으로 불리게 되었습니다.

Transaction은 탈중앙화된 시스템으로 설계되어있기 때문에 모든 Transaction을 노드가 검증하고 “합의”를 통해 Block을 생성하게 됩니다. 이렇게 생성된 Block은 네트워크의 모든 Node에 복제되어 분산 원장을 형성하며 이를 통해 거래의 안정성과 신뢰성을 보장하게 됩니다.

Block의 Structure 및 분석

Block은 크게 Block Header와 Block Body로 구성되어 있습니다. 이더리움 Client인 Go-Ethereum(Geth)에서 확인할 수 있는 Block 구조는 다음과 같습니다.

type **Header** struct {
	ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"`
	UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"`
	Coinbase    common.Address `json:"miner"`
	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        uint64         `json:"timestamp"        gencodec:"required"`
	Extra       []byte         `json:"extraData"        gencodec:"required"`
	MixDigest   common.Hash    `json:"mixHash"`
	Nonce       BlockNonce     `json:"nonce"`

	// BaseFee was added by EIP-1559 and is ignored in legacy headers.
	BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`

	// WithdrawalsHash was added by EIP-4895 and is ignored in legacy headers.
	WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
}

type **Body** struct {
	Transactions []*Transaction
	Uncles       []*Header
	Withdrawals  []*Withdrawal `rlp:"optional"`
}

type **Block** struct {
	header       *Header
	uncles       []*Header
	transactions Transactions
	withdrawals  Withdrawals

	// caches
	hash atomic.Value
	size atomic.Value

	// These fields are used by package eth to track
	// inter-peer block relay.
	ReceivedAt   time.Time
	ReceivedFrom interface{}
}

참조 <https://github.com/ethereum/go-ethereum/blob/master/core/types/block.go>

Block Header

Block Header에는 Block을 구성하고 있는 다양한 값이 기록됩니다. Block Header에 기록되는 값은 다음과 같습니다.

  • ParentHash: 이전 블록의 해시입니다.
  • UnclesHash: 모든 uncle 블록의 해시 값을 포함하는 블록 해시입니다.
  • CoinBase : 블록 채굴 보상이 지급될 주소입니다.
  • StateRoot: 현재 블록의 상태 트리 루트입니다.
  • TxHash: 현재 블록의 모든 트랜잭션에 대한 Merkle 트리의 루트 값 입니다.
  • ReceiptHash: 현재 블록의 모든 트랜잭션 Receipt에 대한 Merkle 트리 루트입니다
  • Bloom: 블록의 모든 로그에 대한 필터값을 의미합니다. 블룸 필터를 사용하여 로그를 검색할 수 있습니다.
  • Difficulty: 현재 블록의 난이도를 의미합니다.
  • Number: 현재 블록의 순서 번호입니다.
  • GasLimit: 현재 블록의 최대 가스 양 입니다.
  • GasUsed: 현재 블록에 포함된 모든 트랜잭션에서 사용된 총 가스 양 입니다.
  • Time: 현재 블록이 생성된 시간을 나타내며 format은 Unix Timestamp 입니다.
  • Extra: 블록의 추가 데이터입니다. 해당 블록의 속성을 나타내는 정보를 저장하며 블록 생성자가 필요에 따라 입력한 값으로 optional한 값 입니다.
  • MixDigest: 채굴에 사용된 mix hash입니다. 마이너는 mixHash 값과 nonce를 변경하며 계산하여 최종적으로 얻어진 블록 해시 값이 난이도 보다 작은 값이 나오도록 계산을 해야 합니다. 만약 mixHash 값이 올바르지 않다면 해시 값이 난이도 보다 낮아질 확률이 거의 없기 떄문에 해당 블록은 유효하지 않은 것으로 간주됩니다.
  • Nonce: 블록 채굴에 사용되는 값으로 MixDigest와 Nonce를 계산하여 얻어진 블록의 해시 값이 difficulty에 지정된 값 보다 작은 값이 나오면 성공적으로 블록을 생성하게 됩니다.
  • baseFeePerGas: London 하드포크 이후 생성된 값으로 프로토콜에 의해 지정되는 가스당 기본 수수료 입니다. (Optional 한 값입니다.)
  • totalDifficulty: 현재 블록의 누적 난이도 값입니다.

Block Body

Block Body에는 현재 블록에 저장되어 있는 트랜잭션이 기록됩니다 Block Body에 기록되는 값은 다음과 같습니다.

  • transactions: 현재 블록에 포함된 모든 트랜잭션 배열입니다. transaction struct를 참조합니다.
  • uncles: 현재 블록의 모든 uncle 블록을 나타내는 블록 배열입니다. uncle 블록이란 채굴에 성공하였으나 블록체인에 포함되지 못한 블록을 의미합니다. 이더리움에서는 이러한 블록에도 일부 보상을 제공하여 채굴자가 공정한 보상을 받을 수 있도록 보완하고 있습니다. Block Header struct를 참조합니다.

Ethereum JSON-RPC를 이용하여 getBlock 호출

RPC는 Remote Procedure Call의 약자로 네트워크를 통해 떨어져 있는 다른 컴퓨터나 서버에 있는 함수나 프로시저를 호출하는 프로토콜입니다. Ethereum JSON-RPC를 이용하여 이더리움과 상호작용이 가능합니다. Ethereum JSON-RPC 중 하나인 eth_getBlockByNumber method를 호출하여 가장 최신 블록의 데이터를 확인해 보도록 하겠습니다.

📘

JSON-RPC 호출을 위한 Node RPC-Endpoint 가져오기

Node를 이용하기 위해서는 해당 Node를 특정하는 RPC-Endpoint가 필요합니다. Nodit Quickstart 페이지를 참고하여 Nodit에서 제공하는 RPC-Endpoint를 가져와주세요.
Nodit Quickstart

curl -X POST "https://ethereum-mainnet.nodit.io/{YOUR_PROJECT}" \
--header 'Content-Type: application/json' \
--data '{
    "jsonrpc":"2.0",
    "method":"eth_getBlockByNumber",
    "params":["latest", true],
    "id":1
}'

다음은 eth_getBlockByNumber의 응답으로 반환받는 값 입니다.

💡 **Response** eth_getBlockByNumber
{
  jsonrpc: '2.0',
  id: 1,
  result: {
    baseFeePerGas: '0x42bd6afa6',
    difficulty: '0x0',
    extraData: '0x6d616e74612d6275696c646572',
    gasLimit: '0x1c9c380',
    gasUsed: '0xdd40cf',
    hash: '0xe5ad8ce4e8b98e578ff8adc1db37abaaa59f0c9768abcf18404faeea8db45fa8',
    logsBloom: '0x6b39547e838c97036419426ebf3c7da213e2db49651e21006a81ae846cbf3f3afad5c151e019cf2a8af5cb16aaffb52e42392ef2de27bcbde0554c17937f0bec24544b54467c86fbee49c26bea39f8bb784faa454d44da8a98d6ba95b4a1a931f61c4a4c131eaae391259c20ad984ebde614cbdf320f6c9e57874ad38ef80a90ce2101eead569408c9d3915797c2c0caa8479ae1ef5c455c2f2634f7629343f89f7d11ef5fa37e09f81adce6de5df2f3bff8ca4564ae49fb9ffd77d6b4bd0041fb7e36ea61482bb0c5d90e932e5f4b54d875a266fb673e748d91fcb3ab1e661e93d6f2b9bedc048d12379ab3e38982e0972a7654fd7ce553819d753faab06d47',
    miner: '0x5f927395213ee6b95de97bddcb1b2b1c0f16844f',
    mixHash: '0xc3e1ad139a26d9dff9450411a997a2c5f4d4e7bd770d4d52b0b18ed533df5ce3',
    nonce: '0x0000000000000000',
    number: '0x102e6c4',
    parentHash: '0xc725f989050cd8ceb8d2190ec38c45c30c37293a40755d1f41c28fff948e7b67',
    receiptsRoot: '0x1115efbc1c86c55f6c15f447ed47717df0858b9df3a6ff46ae6043323ab07f86',
    sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
    size: '0x22129',
    stateRoot: '0x12046ad361b63ae23c5778c1809e6801589848f209ad8d2fff5b6418e232994d',
    timestamp: '0x642a902b',
    totalDifficulty: '0xc70d815d562d3cfa955',
    transactions: [
      [Object], ... , [Object],
      ... 30 more items
    ],
    transactionsRoot: '0x0b9beb7315a47aff52d19b3c6e3fccdf4906c5ad84fff1a09723c158c525e716',
    uncles: []
  }
}

실제 호출해서 받은 응답 값과 Block struct의 Field 값을 매핑하면 다음과 같습니다. Block Structure와 실제 호출한 Block의 Field가 일치하는 것이 보이시나요?

      Go-Ethereum struct                eth_getBlockByNumber의 Response Field	

	ParentHash  common.Hash         =        parentHash
	UncleHash   common.Hash         =        sha3Uncles
	Coinbase    common.Address      =        miner
	Root        common.Hash         =        stateRoot
	TxHash      common.Hash         =        transactionsRoot
	ReceiptHash common.Hash         =        receiptsRoot
	Bloom       Bloom               =        logsBloom
	Difficulty  *big.Int            =        totalDifficulty
	Number      *big.Int            =        number
	GasLimit    uint64              =        gasLimit
	GasUsed     uint64              =        gasUsed
	Time        uint64              =        timestamp
	Extra       []byte              =        extraData
	MixDigest   common.Hash         =        mixHash
	Nonce       BlockNonce          =        nonce

  Transactions                    =        transactions
  Uncles                          =        sha3Uncles
	BaseFee *big.Int                =        baseFeePerGas


Nodit Web3 Data API를 이용한 Block 정보 조회

아래 Nodit API들을 활용하여 Block 정보를 조회할 수 있습니다. API 상세 페이지에서 API를 호출해보고 응답에 포함된 블록 데이터를 바로 확인해볼 수 있습니다.