목적 및 범위
이 문서는 StableNet 블록체인의 블록 및 헤더 구조, 인코딩 방식, 해시 계산, 머클 루트 구성, 그리고 WBFT 합의에 특화된 Extra 데이터 구조를 설명합니다.블록이 어떻게 구성되고 검증되며, 합의 과정에서 어떤 데이터가 헤더에 포함되는지를 중심으로 다룹니다. 관련 문서:
- 트랜잭션 데이터 구조: 트랜잭션 유형 및 인코딩
- 블록 검증 및 체인 삽입: 블록체인 관리
- 블록 생성 프로세스: 블록 생성
블록 구조 개요
블록은 메타데이터를 포함하는 헤더, 트랜잭션을 포함하는 본문, 선택적 인출(withdrawals) 목록과 엉클(uncles) 목록으로 구성됩니다.StableNet(WBFT 기반)에서는 엉클을 사용하지 않으며, 항상 비어 있습니다.
Block 타입
Block 구조체(core/types/block.go)의 주요 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
header | *Header | 메타데이터 및 머클 루트를 포함한 블록 헤더 |
transactions | Transactions | 블록에 포함된 트랜잭션 목록 |
uncles | []*Header | 엉클 블록 헤더 (사용안함) |
withdrawals | Withdrawals | 인출 목록 (Shanghai 이후 선택적) |
hash | atomic.Value | 캐시된 블록 해시 |
size | atomic.Value | 캐시된 RLP 인코딩 크기 |
헤더 구조
Header는 블록의 핵심 메타데이터로, 블록 내용과 실행 결과에 대한 암호학적 커밋을 포함합니다.
핵심 헤더 필드
| 필드 | 타입 | 설명 |
|---|---|---|
ParentHash | common.Hash | 부모 블록 헤더 해시 |
UncleHash | common.Hash | 엉클 목록 해시 (StableNet에서는 EmptyUncleHash) |
Coinbase | common.Address | 블록 수수료 수령 주소(검증자 주소) |
Root | common.Hash | 블록 실행 후 상태 trie 루트 |
TxHash | common.Hash | 트랜잭션 머클 루트 |
ReceiptHash | common.Hash | 영수증 머클 루트 |
Bloom | Bloom | 로그 블룸 필터 |
Difficulty | *big.Int | 블록 난이도 (WBFT: 항상 1) |
Number | *big.Int | 블록 번호 |
GasLimit | uint64 | 블록 가스 한도 |
GasUsed | uint64 | 사용된 총 가스 |
Time | uint64 | 블록 생성 시각 |
Extra | []byte | WBFT 합의 데이터 (RLP 인코딩) |
MixDigest | common.Hash | PoW 필드 (WBFT에서는 사용되지 않음) |
Nonce | BlockNonce | PoW 필드 (WBFT에서는 사용되지 않음) |
Difficulty는 항상 1로 설정되어 WBFT 블록임을 식별합니다Coinbase는 검증자의 합의 서명 주소이며 운영자 주소와 다를 수 있습니다Extra에는 WBFT 합의 라운드 정보, 집계 서명, 가스 팁, 에포크 정보가 포함됩니다GasLimit은 체인 파라미터에 의해 고정된 값으로 운영됩니다
포크별 선택적 필드
다음 필드들은 특정 포크 이후에만 존재하며, nil이 아닌 경우에만 RLP 인코딩에 포함됩니다.| 필드 | EIP | 포크 | 설명 |
|---|---|---|---|
BaseFee | EIP-1559 | London | 블록 기본 수수료 |
WithdrawalsHash | EIP-4895 | Shanghai | 인출 머클 루트 |
BlobGasUsed | EIP-4844 | Cancun | blob 가스 사용량 |
ExcessBlobGas | EIP-4844 | Cancun | 초과 blob 가스 |
ParentBeaconRoot | EIP-4788 | Cancun | 비콘 체인 부모 루트 |
본문 구조
블록 본문은 실제 실행 데이터로 구성됩니다.- Transactions: 트랜잭션 목록
- Uncles: 엉클 헤더 목록 (사용 안함)
- Withdrawals: 인출 목록 (해당 포크 이후)
RLP 인코딩
블록과 헤더는 모두 Recursive Length Prefix(RLP) 인코딩을 사용합니다.블록 인코딩
블록은 다음과 같은 RLP 리스트로 인코딩됩니다.withdrawals 항목이 존재하지 않습니다.
헤더 인코딩
헤더는 정의된 필드 순서대로 RLP 인코딩되며, 선택적 필드는 값이 존재할 때만 포함됩니다.블록 및 헤더 해싱
표준 해싱
블록 해시는 헤더만을 기준으로 계산됩니다.WBFT 전용 헤더 해싱
WBFT에서는 합의 서명이 헤더의Extra 필드에 포함되므로, 서명 대상 해시 계산 시 서명 자체를 제외해야 합니다.
이를 위해 WBFTFilteredHeader()가 사용됩니다.
PreparedSeal,CommittedSeal제거- 라운드 번호, 에포크 정보, 가스 팁은 유지
Difficulty == 1인 경우에만 적용
라운드 포함 해시
WBFT 합의 라운드에서는 라운드 번호를 포함한 별도의 해시를 사용하여, 동일 블록 제안이라도 라운드별로 다른 서명 대상이 됩니다.WBFT Extra 데이터 구조
WBFT 합의 데이터는 헤더의Extra 필드에 RLP 인코딩되어 저장됩니다.
WBFTExtra 구조
| 필드 | 타입 | 설명 |
|---|---|---|
VanityData | []byte | vanity 데이터 |
RandaoReveal | []byte | 랜덤성용 BLS 서명 |
PrevRound | uint32 | 이전 합의 라운드 |
PrevPreparedSeal | *WBFTAggregatedSeal | 이전 prepare 집계 서명 |
PrevCommittedSeal | *WBFTAggregatedSeal | 이전 commit 집계 서명 |
Round | uint32 | 현재 합의 라운드 |
PreparedSeal | *WBFTAggregatedSeal | 현재 prepare 집계 서명 |
CommittedSeal | *WBFTAggregatedSeal | 현재 commit 집계 서명 |
GasTip | *big.Int | 거버넌스가 결정한 가스 팁 |
EpochInfo | *EpochInfo | 다음 에포크 정보 (에포크 블록에서만 포함) |
WBFTAggregatedSeal
여러 검증자의 BLS 서명을 하나로 집계한 구조입니다.| 필드 | 타입 | 설명 |
|---|---|---|
Sealers | SealerSet | 서명한 검증자 비트맵 |
Signature | []byte | 집계된 BLS 서명 |
EpochInfo
에포크 블록에만 포함되며, 다음 에포크의 검증자 세트를 정의합니다.| 필드 | 타입 | 설명 |
|---|---|---|
Candidates | []*Candidate | 모든 검증자 후보 |
Validators | []uint32 | 활성 검증자 인덱스 |
BLSPublicKeys | [][]byte | 검증자 BLS 공개 키 |
머클 루트 계산
TxHash
트랜잭션 목록을 Merkle Patricia Trie로 구성하여 계산됩니다.빈 목록은
EmptyTxsHash를 사용합니다.
ReceiptHash
영수증 목록으로 동일한 방식으로 계산되며, 블룸 필터는 영수증에서 파생됩니다.UncleHash
엉클 헤더 목록의 해시이며, StableNet에서는 항상EmptyUncleHash입니다.
WithdrawalsHash
Shanghai 이후 블록에서 인출 목록에 대해 계산됩니다.블록 구성
NewBlock 함수
NewBlock()은 다음 절차로 블록을 생성합니다.
- 헤더 복사
- 트랜잭션 머클 루트 계산
- 영수증 루트 및 블룸 계산
- 엉클 해시 계산
- 본문 데이터 결합
Difficulty = 1Extra에 WBFTExtra 인코딩- 에포크 블록에서만
EpochInfo포함
NewBlockWithWithdrawals()를 사용합니다.
헤더 무결성 검사
Header.SanityCheck()는 비정상 입력으로 인한 DoS를 방지합니다.
| 필드 | 제한 |
|---|---|
Number | 64비트 범위 |
Difficulty | 합리적 비트 길이 |
Extra | 최대 크기 제한 |
BaseFee | uint256 범위 |
블록 크기 계산
블록 크기는 RLP 인코딩된 전체 크기이며,Block.Size()로 조회합니다.Blob 트랜잭션이 포함된 경우 사이드카 데이터도 크기에 포함됩니다.

