Skip to content

cosmos-study/cometbft-study

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 

Repository files navigation

버전: CometBFT v0.38+ / v1.0 κΈ°μ€€ | μ΅œμ’… μ—…λ°μ΄νŠΈ: 2025λ…„


λͺ©μ°¨

  1. CometBFT κ°œμš”
  2. Tendermint μ•Œκ³ λ¦¬μ¦˜
  3. State ꡬ쑰체
  4. μ»¨μ„Όμ„œμŠ€ 흐름
  5. ABCI 2.0 (ABCI++)
  6. WAL (Write-Ahead Log)
  7. Proposer μ„ μ •
  8. νƒ€μž„μ•„μ›ƒ μ„€μ •
  9. Evidence 처리
  10. 블둝 쑰각과 P2P
  11. Mempool
  12. 정리

1. CometBFT κ°œμš”

CometBFTλŠ” Tendermint Core의 곡식 후속 ν”„λ‘œμ νŠΈλ‘œ, Byzantine Fault Tolerant μ»¨μ„Όμ„œμŠ€ μ—”μ§„μž…λ‹ˆλ‹€. Cosmos SDK와 ν•¨κ»˜ μ‚¬μš©λ˜μ–΄ 수백 개의 블둝체인을 μ§€μ›ν•©λ‹ˆλ‹€.

핡심 νŠΉμ§•

  • μ¦‰μ‹œ μ΅œμ’…μ„±(Instant Finality): 블둝이 μ»€λ°‹λ˜λ©΄ 되돌릴 수 μ—†μŒ
  • Byzantine Fault Tolerance: μ΅œλŒ€ f < n/3 μ•…μ˜μ  λ…Έλ“œ ν—ˆμš© (n λ…Έλ“œ 쀑 f μ•…μ„±)
  • Partial Synchrony: GST(Global Stabilization Time) 이후 동기화 κ°€μ •
  • ABCI 2.0: PrepareProposal, ProcessProposal, Vote Extensions 지원

μ•„ν‚€ν…μ²˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         CometBFT Node                                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                      Consensus Engine                          β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚  β”‚
β”‚  β”‚  β”‚ State    β”‚  β”‚ Reactor  β”‚  β”‚ WAL      β”‚  β”‚ Evidence β”‚       β”‚  β”‚
β”‚  β”‚  β”‚ Machine  β”‚  β”‚ (P2P)    β”‚  β”‚          β”‚  β”‚ Pool     β”‚       β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         β”‚                                                            β”‚
β”‚         β”‚ ABCI 2.0 (gRPC/Socket)                                     β”‚
β”‚         β–Ό                                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    Application (Cosmos SDK)                    β”‚  β”‚
β”‚  β”‚  PrepareProposal β†’ ProcessProposal β†’ ExtendVote β†’ FinalizeBlockβ”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         β”‚                                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
β”‚  β”‚  Mempool     β”‚    β”‚  Block Store β”‚    β”‚  State Store β”‚           β”‚
β”‚  β”‚  (TxPool)    β”‚    β”‚  (Blocks)    β”‚    β”‚  (AppState)  β”‚           β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚
β”‚                                                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    P2P Network Layer                           β”‚  β”‚
β”‚  β”‚        Gossip Protocol / Connection Management                 β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

μ½”λ“œ ꡬ쑰

github.com/cometbft/cometbft

cometbft/
β”œβ”€β”€ // 핡심 μ»¨μ„Όμ„œμŠ€
β”œβ”€β”€ consensus/
β”‚   β”œβ”€β”€ state.go          // State ꡬ쑰체, enterPropose, enterPrevote λ“±
β”‚   β”œβ”€β”€ reactor.go        // P2P λ©”μ‹œμ§€ 처리
β”‚   β”œβ”€β”€ wal.go            // Write-Ahead Log
β”‚   └── types/
β”‚       └── round_state.go // RoundState, HeightVoteSet
β”‚
β”œβ”€β”€ // ABCI μΈν„°νŽ˜μ΄μŠ€
β”œβ”€β”€ abci/
β”‚   β”œβ”€β”€ types/
β”‚   β”‚   └── types.pb.go   // ABCI λ©”μ‹œμ§€ μ •μ˜
β”‚   └── client/           // ABCI ν΄λΌμ΄μ–ΈνŠΈ
β”‚
β”œβ”€β”€ // μƒνƒœ 관리
β”œβ”€β”€ state/
β”‚   β”œβ”€β”€ state.go          // 블둝체인 μƒνƒœ
β”‚   β”œβ”€β”€ execution.go      // 블둝 μ‹€ν–‰
β”‚   └── store.go          // μƒνƒœ μ €μž₯μ†Œ
β”‚
β”œβ”€β”€ // νƒ€μž… μ •μ˜
β”œβ”€β”€ types/
β”‚   β”œβ”€β”€ block.go          // Block, Header
β”‚   β”œβ”€β”€ vote.go           // Vote, VoteSet
β”‚   β”œβ”€β”€ proposal.go       // Proposal
β”‚   β”œβ”€β”€ validator.go      // Validator, ValidatorSet
β”‚   └── evidence.go       // Evidence (μ΄μ€‘μ„œλͺ… 증거)
β”‚
β”œβ”€β”€ // P2P λ„€νŠΈμ›Œν‚Ή
β”œβ”€β”€ p2p/
β”‚   β”œβ”€β”€ switch.go         // P2P Switch (ν”Όμ–΄ 관리)
β”‚   β”œβ”€β”€ conn/             // MConnection (λ©€ν‹°ν”Œλ ‰μŠ€)
β”‚   └── pex/              // Peer Exchange
β”‚
β”œβ”€β”€ // Mempool
β”œβ”€β”€ mempool/
β”‚   β”œβ”€β”€ clist_mempool.go  // κΈ°λ³Έ Mempool κ΅¬ν˜„
β”‚   └── reactor.go        // Mempool P2P
β”‚
└── // Evidence
    evidence/
    β”œβ”€β”€ pool.go           // Evidence Pool
    └── reactor.go        // Evidence P2P

버전별 ABCI 비ꡐ

v0.34 (Legacy) v0.37 v0.38+ / v1.0 (μ΅œμ‹ )
BeginBlock + PrepareProposal PrepareProposal
DeliverTx Γ— N + ProcessProposal ProcessProposal
EndBlock BeginBlock/EndBlock ExtendVote
Commit VerifyVoteExtension
FinalizeBlock
Commit

2. Tendermint μ•Œκ³ λ¦¬μ¦˜

TendermintλŠ” PBFT κ³„μ—΄μ˜ BFT μ»¨μ„Όμ„œμŠ€ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ, DLS(Dwork-Lynch-Stockmeyer) μ•Œκ³ λ¦¬μ¦˜μ—μ„œ μ˜κ°μ„ λ°›μ•˜μŠ΅λ‹ˆλ‹€.

ν•©μ˜ 속성

속성 μ„€λͺ… 보μž₯ 쑰건
Safety 두 개의 λ‹€λ₯Έ 블둝이 같은 λ†’μ΄μ—μ„œ μ»€λ°‹λ˜μ§€ μ•ŠμŒ f < n/3 (항상)
Liveness κ²°κ΅­ μƒˆ 블둝이 컀밋됨 f < n/3 + λΆ€λΆ„ 동기 (GST 이후)
Validity μ»€λ°‹λœ 블둝은 μœ νš¨ν•œ νŠΈλžœμž­μ…˜λ§Œ 포함 μ •μ§ν•œ μ œμ•ˆμž

μ»¨μ„Όμ„œμŠ€ λΌμš΄λ“œ ꡬ쑰

Height H의 μ»¨μ„Όμ„œμŠ€:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          Round 0                                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                     β”‚
β”‚   NewHeight ──→ Propose ──→ Prevote ──→ Precommit ──→ Commit       β”‚
β”‚       β”‚           β”‚           β”‚            β”‚            β”‚          β”‚
β”‚       β”‚      ν”„λ‘œν¬μ €κ°€    블둝 검증 ν›„   Prevote κ²°κ³Ό  2/3+ λͺ¨μ΄λ©΄   β”‚
β”‚       β”‚      블둝 μ œμ•ˆ    1μ°¨ νˆ¬ν‘œ     기반 2μ°¨ νˆ¬ν‘œ   블둝 ν™•μ •      β”‚
β”‚       β”‚                                                             β”‚
β”‚       β”‚         (μ‹€νŒ¨ μ‹œ)                                           β”‚
β”‚       β”‚            ↓                                                β”‚
β”‚       └──────→ Round 1 ──→ Round 2 ──→ ...                         β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

νƒ€μž„μ•„μ›ƒ:
β€’ ProposeTimeout: μ œμ•ˆ λŒ€κΈ° (κΈ°λ³Έ 3초)
β€’ PrevoteTimeout: Prevote μˆ˜μ§‘ λŒ€κΈ° (+2/3 Any ν›„)
β€’ PrecommitTimeout: Precommit μˆ˜μ§‘ λŒ€κΈ° (+2/3 Any ν›„)

Polka와 Lock λ©”μ»€λ‹ˆμ¦˜

핡심 μš©μ–΄

  • Polka: νŠΉμ • 블둝(λ˜λŠ” nil)에 λŒ€ν•œ +2/3 Prevote μ§‘ν•©
  • PoLC (Proof of Lock Change): Lock을 λ³€κ²½ν•˜κ±°λ‚˜ ν•΄μ œν•  수 μžˆλŠ” 증거
  • Lock: κ²€μ¦μžκ°€ νŠΉμ • 블둝에 "μž κΉ€" (λ‹€λ₯Έ 블둝에 Prevote λΆˆκ°€)

Lock κ·œμΉ™ (consensus/state.go)

// Lock κ·œμΉ™:
// 1. +2/3 Prevoteλ₯Ό λ³Έ 블둝에 Lock
// 2. Lock된 λΈ”λ‘μ—λ§Œ Precommit κ°€λŠ₯
// 3. 더 높은 λΌμš΄λ“œμ˜ PoLCκ°€ μžˆμ–΄μ•Ό Lock ν•΄μ œ κ°€λŠ₯

func (cs *State) enterPrecommit(height int64, round int32) {
    // Prevote κ²°κ³Ό 확인
    blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority()
    
    if !ok {
        // +2/3 μ—†μŒ β†’ nil Precommit
        cs.signAddVote(PrecommitType, nil)
        return
    }
    
    if blockID.IsNil() {
        // +2/3κ°€ nil 선택 β†’ Lock ν•΄μ œ
        cs.LockedRound = -1
        cs.LockedBlock = nil
        cs.signAddVote(PrecommitType, nil)
        return
    }
    
    // +2/3κ°€ νŠΉμ • 블둝 선택 β†’ Lock & Precommit!
    if cs.ProposalBlock.HashesTo(blockID.Hash) {
        cs.LockedRound = round
        cs.LockedBlock = cs.ProposalBlock
        cs.signAddVote(PrecommitType, blockID.Hash)
    }
}

Lock이 Safetyλ₯Ό 보μž₯ν•˜λŠ” 이유

μ‹œλ‚˜λ¦¬μ˜€: Round 0μ—μ„œ Block A에 Lock

Round 0:
  β€’ κ²€μ¦μž 1,2,3이 Block A에 Prevote (+2/3)
  β€’ κ²€μ¦μž 1,2,3이 Block A에 Lock
  β€’ λ„€νŠΈμ›Œν¬ μ§€μ—°μœΌλ‘œ Precommit μˆ˜μ§‘ μ‹€νŒ¨

Round 1:
  β€’ μƒˆ μ œμ•ˆμžκ°€ Block B μ œμ•ˆ
  β€’ κ²€μ¦μž 1,2,3: "λ‚˜λŠ” Block A에 Lockλ˜μ–΄ μžˆμ–΄!"
  β€’ Block A에 λŒ€ν•œ PoLC μ—†μ΄λŠ” Block B에 Prevote λΆˆκ°€
  
κ²°κ³Ό: 
  β€’ Safety 보μž₯: Block A와 Block B λ™μ‹œ 컀밋 λΆˆκ°€λŠ₯
  β€’ μ •μ§ν•œ λ…Έλ“œκ°€ 2/3 이상이면, κ²°κ΅­ 같은 블둝에 ν•©μ˜

νˆ¬ν‘œ κ·œμΉ™ 상세

consensus/state.go - defaultDoPrevote

func (cs *State) defaultDoPrevote(height int64, round int32) {
    // κ·œμΉ™ 1: Lock된 블둝이 있으면 κ·Έ 블둝에 νˆ¬ν‘œ
    if cs.LockedBlock != nil {
        cs.signAddVote(PrevoteType, cs.LockedBlock.Hash())
        return
    }

    // κ·œμΉ™ 2: μ œμ•ˆλœ 블둝이 μ—†μœΌλ©΄ nil νˆ¬ν‘œ
    if cs.ProposalBlock == nil {
        cs.signAddVote(PrevoteType, nil)
        return
    }

    // κ·œμΉ™ 3: 블둝 검증
    err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock)
    if err != nil {
        cs.signAddVote(PrevoteType, nil)
        return
    }

    // κ·œμΉ™ 4: μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 검증 (ProcessProposal)
    isAppValid, err := cs.blockExec.ProcessProposal(cs.ProposalBlock, cs.state)
    if err != nil || !isAppValid {
        cs.signAddVote(PrevoteType, nil)
        return
    }

    // λͺ¨λ“  검증 톡과! 블둝에 νˆ¬ν‘œ
    cs.signAddVote(PrevoteType, cs.ProposalBlock.Hash())
}

3. State ꡬ쑰체

RoundState - λΌμš΄λ“œ μƒνƒœ

consensus/types/round_state.go

// RoundStateλŠ” μ»¨μ„Όμ„œμŠ€μ˜ ν˜„μž¬ μƒνƒœλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€
type RoundState struct {
    // μœ„μΉ˜ 정보
    Height    int64         // ν˜„μž¬ 블둝 높이
    Round     int32         // ν˜„μž¬ λΌμš΄λ“œ (0λΆ€ν„° μ‹œμž‘)
    Step      RoundStepType // ν˜„μž¬ 단계

    // μ‹œκ°„ 정보
    StartTime     time.Time  // λΌμš΄λ“œ μ‹œμž‘ μ‹œκ°„
    CommitTime    time.Time  // 컀밋 μ‹œκ°„

    // κ²€μ¦μž 정보
    Validators           *types.ValidatorSet
    Proposer             *types.Validator

    // μ œμ•ˆ 정보
    Proposal             *types.Proposal
    ProposalBlock        *types.Block
    ProposalBlockParts   *types.PartSet

    // Lock 정보 (Safety 핡심!)
    LockedRound          int32
    LockedBlock          *types.Block
    LockedBlockParts     *types.PartSet

    // Valid 정보 (Precommit 없이 +2/3 Prevote λ³Έ 경우)
    ValidRound           int32
    ValidBlock           *types.Block
    ValidBlockParts      *types.PartSet

    // νˆ¬ν‘œ 정보
    Votes                *HeightVoteSet

    // 이전 높이 컀밋
    LastCommit           *types.ExtendedCommit
}

// RoundStepType μ •μ˜
const (
    RoundStepNewHeight     = 0x01 // μƒˆ 높이 λŒ€κΈ°
    RoundStepNewRound      = 0x02 // μƒˆ λΌμš΄λ“œ μ‹œμž‘
    RoundStepPropose       = 0x03 // μ œμ•ˆ 단계
    RoundStepPrevote       = 0x04 // Prevote 단계
    RoundStepPrevoteWait   = 0x05 // +2/3 Any λŒ€κΈ°
    RoundStepPrecommit     = 0x06 // Precommit 단계
    RoundStepPrecommitWait = 0x07 // +2/3 Any λŒ€κΈ°
    RoundStepCommit        = 0x08 // 컀밋 μ™„λ£Œ
)

VoteSet - νˆ¬ν‘œ μ§‘ν•©

types/vote_set.go

// VoteSet은 νŠΉμ • (Height, Round, Type)의 λͺ¨λ“  νˆ¬ν‘œλ₯Ό 관리
type VoteSet struct {
    chainID       string
    height        int64
    round         int32
    signedMsgType SignedMsgType  // Prevote or Precommit
    valSet        *ValidatorSet

    mtx           sync.Mutex
    votesBitArray *bits.BitArray  // μ–΄λ–€ κ²€μ¦μžκ°€ νˆ¬ν‘œν–ˆλŠ”μ§€
    votes         []*Vote         // 인덱슀 = κ²€μ¦μž 인덱슀
    sum           int64           // 총 νˆ¬ν‘œλ ₯

    // 블둝별 νˆ¬ν‘œλ ₯ 좔적
    votesByBlock  map[string]*blockVotes

    // +2/3 달성 μ—¬λΆ€
    maj23         *BlockID  // nil이 μ•„λ‹ˆλ©΄ +2/3 달성
}

// +2/3 확인 λ©”μ„œλ“œ
func (voteSet *VoteSet) TwoThirdsMajority() (BlockID, bool) {
    if voteSet == nil {
        return BlockID{}, false
    }
    voteSet.mtx.Lock()
    defer voteSet.mtx.Unlock()
    
    if voteSet.maj23 != nil {
        return *voteSet.maj23, true
    }
    return BlockID{}, false
}

// +2/3 Any (nil 포함 μ•„λ¬΄κ±°λ‚˜) 확인
func (voteSet *VoteSet) HasTwoThirdsAny() bool {
    if voteSet == nil {
        return false
    }
    voteSet.mtx.Lock()
    defer voteSet.mtx.Unlock()
    
    return voteSet.sum > voteSet.valSet.TotalVotingPower()*2/3
}

HeightVoteSet - 높이별 λͺ¨λ“  νˆ¬ν‘œ

consensus/types/height_vote_set.go

// HeightVoteSet은 ν•œ λ†’μ΄μ˜ λͺ¨λ“  λΌμš΄λ“œ νˆ¬ν‘œλ₯Ό 관리
type HeightVoteSet struct {
    chainID string
    height  int64
    valSet  *types.ValidatorSet

    mtx               sync.Mutex
    round             int32             // μ•Œλ €μ§„ μ΅œλŒ€ λΌμš΄λ“œ
    roundVoteSets     map[int32]RoundVoteSet  // λΌμš΄λ“œλ³„ VoteSet
    peerCatchupRounds map[p2p.ID][]int32 // 피어별 μΊμΉ˜μ—… λΌμš΄λ“œ
}

type RoundVoteSet struct {
    Prevotes   *types.VoteSet
    Precommits *types.VoteSet
}

// νŠΉμ • λΌμš΄λ“œμ˜ Prevote κ°€μ Έμ˜€κΈ°
func (hvs *HeightVoteSet) Prevotes(round int32) *types.VoteSet {
    hvs.mtx.Lock()
    defer hvs.mtx.Unlock()
    return hvs.getVoteSet(round, types.PrevoteType)
}

4. μ»¨μ„Όμ„œμŠ€ 흐름

receiveRoutine - 메인 이벀트 루프

consensus/state.go

// receiveRoutine은 μ»¨μ„Όμ„œμŠ€μ˜ 심μž₯λΆ€μž…λ‹ˆλ‹€
func (cs *State) receiveRoutine(maxSteps int) {
    defer func() {
        if r := recover(); r != nil {
            cs.Logger.Error("CONSENSUS FAILURE!", "err", r)
        }
    }()

    for {
        rs := cs.RoundState
        var mi msgInfo

        select {
        // 1. νŠΈλžœμž­μ…˜ μ‚¬μš© κ°€λŠ₯ μ•Œλ¦Ό
        case <-cs.txNotifier.TxsAvailable():
            cs.handleTxsAvailable()

        // 2. ν”Όμ–΄λ‘œλΆ€ν„° λ©”μ‹œμ§€ (Proposal, Vote λ“±)
        case mi = <-cs.peerMsgQueue:
            cs.wal.Write(mi)      // WAL에 기둝 (비동기)
            cs.handleMsg(mi)

        // 3. λ‚΄λΆ€ λ©”μ‹œμ§€ (μžμ‹ μ˜ νˆ¬ν‘œ)
        case mi = <-cs.internalMsgQueue:
            cs.wal.WriteSync(mi)  // WAL에 기둝 (동기 - fsync!)
            cs.handleMsg(mi)

        // 4. νƒ€μž„μ•„μ›ƒ λ°œμƒ
        case ti := <-cs.timeoutTicker.Chan():
            cs.wal.Write(ti)
            cs.handleTimeout(ti, rs)

        // 5. μ’…λ£Œ μ‹ ν˜Έ
        case <-cs.Quit():
            return
        }
    }
}

μƒνƒœ 전이 ν•¨μˆ˜λ“€

enterNewRound - μƒˆ λΌμš΄λ“œ μ‹œμž‘

func (cs *State) enterNewRound(height int64, round int32) {
    // μƒνƒœ 검증
    if cs.Height != height || round < cs.Round || 
       (cs.Round == round && cs.Step != RoundStepNewHeight) {
        return
    }

    cs.Logger.Info("enterNewRound", 
        "height", height, 
        "round", round)

    // λΌμš΄λ“œ μƒνƒœ μ—…λ°μ΄νŠΈ
    cs.Round = round
    cs.Step = RoundStepNewRound
    cs.Proposal = nil
    cs.ProposalBlock = nil
    cs.ProposalBlockParts = nil

    // μƒˆ λΌμš΄λ“œμ˜ μ œμ•ˆμž κ²°μ •
    cs.Validators.IncrementProposerPriority(round)
    cs.Proposer = cs.Validators.GetProposer()

    // μ¦‰μ‹œ Propose λ‹¨κ³„λ‘œ
    cs.enterPropose(height, round)
}

enterPropose - 블둝 μ œμ•ˆ

func (cs *State) enterPropose(height int64, round int32) {
    // Propose νƒ€μž„μ•„μ›ƒ μ„€μ •
    cs.scheduleTimeout(cs.config.Propose(round), height, round, RoundStepPropose)

    cs.Step = RoundStepPropose

    // λ‚΄κ°€ 이 λΌμš΄λ“œμ˜ μ œμ•ˆμžμΈκ°€?
    if cs.isProposer(cs.privValidatorPubKey.Address()) {
        cs.Logger.Info("enterPropose: Our turn to propose")
        cs.decideProposal(height, round)
    } else {
        cs.Logger.Info("enterPropose: Not our turn",
            "proposer", cs.Proposer.Address)
    }
}

func (cs *State) decideProposal(height int64, round int32) {
    var block *types.Block
    var blockParts *types.PartSet

    // ValidBlock이 있으면 그것 μ‚¬μš© (이전 λΌμš΄λ“œμ—μ„œ +2/3 Prevote 받은 블둝)
    if cs.ValidBlock != nil {
        block = cs.ValidBlock
        blockParts = cs.ValidBlockParts
    } else {
        // μƒˆ 블둝 생성 (PrepareProposal 호좜)
        block, blockParts = cs.createProposalBlock()
    }

    // Proposal 생성 및 μ„œλͺ…
    proposal := types.NewProposal(height, round, cs.ValidRound, blockParts.Header())
    cs.privValidator.SignProposal(cs.state.ChainID, proposal)

    // λΈŒλ‘œλ“œμΊμŠ€νŠΈ
    cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""})
    for i := 0; i < int(blockParts.Total()); i++ {
        part := blockParts.GetPart(i)
        cs.sendInternalMessage(msgInfo{&BlockPartMessage{height, round, part}, ""})
    }
}

enterPrecommit - 2μ°¨ νˆ¬ν‘œ

func (cs *State) enterPrecommit(height int64, round int32) {
    cs.Step = RoundStepPrecommit

    // Prevote κ²°κ³Ό 확인
    blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority()

    if !ok {
        // +2/3 Prevote μ—†μŒ β†’ nil Precommit
        cs.signAddVote(PrecommitType, nil)
        return
    }

    // *** Vote Extension 처리 (v0.38+) ***
    if cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) {
        // ExtendVote 호좜
        extension, err := cs.blockExec.ExtendVote(
            cs.ProposalBlock, 
            cs.state,
        )
        if err == nil {
            vote.Extension = extension
        }
    }

    // Lock μ—…λ°μ΄νŠΈ 및 Precommit
    if !blockID.IsNil() && cs.ProposalBlock.HashesTo(blockID.Hash) {
        cs.LockedRound = round
        cs.LockedBlock = cs.ProposalBlock
        cs.signAddVote(PrecommitType, blockID.Hash)
    } else {
        cs.LockedRound = -1
        cs.LockedBlock = nil
        cs.signAddVote(PrecommitType, nil)
    }
}

finalizeCommit - 블둝 ν™•μ •

func (cs *State) finalizeCommit(height int64) {
    block := cs.ProposalBlock
    blockParts := cs.ProposalBlockParts

    // 1. 블둝 μ €μž₯
    cs.blockStore.SaveBlock(block, blockParts, cs.Votes.Precommits(cs.CommitRound))

    // 2. WAL에 EndHeight 기둝
    cs.wal.WriteSync(EndHeightMessage{height})

    // 3. 블둝 μ‹€ν–‰! (FinalizeBlock 호좜)
    stateCopy, err := cs.blockExec.ApplyVerifiedBlock(
        cs.state,
        types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()},
        block,
    )

    // 4. μƒνƒœ μ—…λ°μ΄νŠΈ
    cs.updateToState(stateCopy)

    // 5. Mempool μ—…λ°μ΄νŠΈ
    cs.mempool.Update(...)

    // 6. λ‹€μŒ λ†’μ΄λ‘œ
    cs.scheduleRound0(&cs.RoundState)
}

전체 흐름 λ‹€μ΄μ–΄κ·Έλž¨

                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚                    Height H                         β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚
                                               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Round R                                                                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚  β”‚NewRound β”‚ β†’  β”‚                      PROPOSE                                β”‚     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  β€’ μ œμ•ˆμž: PrepareProposal β†’ 블둝 생성                       β”‚     β”‚
β”‚                 β”‚  β€’ λΉ„μ œμ•ˆμž: νƒ€μž„μ•„μ›ƒ λŒ€κΈ° (ProposeTimeout)                   β”‚     β”‚
β”‚                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β”‚                                            β”‚ 블둝 μˆ˜μ‹  λ˜λŠ” νƒ€μž„μ•„μ›ƒ                 β”‚
β”‚                                            β–Ό                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                           PREVOTE (1μ°¨ νˆ¬ν‘œ)                                 β”‚   β”‚
β”‚  β”‚  β€’ ProcessProposal둜 블둝 검증                                               β”‚   β”‚
β”‚  β”‚  β€’ 검증 성곡: 블둝 ν•΄μ‹œμ— νˆ¬ν‘œ / μ‹€νŒ¨: nil νˆ¬ν‘œ                                β”‚   β”‚
β”‚  β”‚  β€’ Lock된 블둝 있으면 κ·Έ 블둝에 νˆ¬ν‘œ                                          β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                   β”‚ νˆ¬ν‘œ μˆ˜μ§‘                                       β”‚
β”‚                                   β–Ό                                                β”‚
β”‚            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚            β”‚            +2/3 Prevotes μˆ˜μ§‘ μ™„λ£Œ?                   β”‚                 β”‚
β”‚            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚                    β”‚                β”‚                  β”‚                            β”‚
β”‚         +2/3 Block A        +2/3 nil          +2/3 미달성                           β”‚
β”‚                    β”‚                β”‚                  β”‚                            β”‚
β”‚                    β–Ό                β–Ό                  β–Ό                            β”‚
β”‚              Lock Block A     Unlock            PrevoteWait                         β”‚
β”‚                    β”‚                β”‚           (νƒ€μž„μ•„μ›ƒ)                           β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                            β”‚
β”‚                                   β”‚                                                 β”‚
β”‚                                   β–Ό                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                         PRECOMMIT (2μ°¨ νˆ¬ν‘œ)                                 β”‚   β”‚
β”‚  β”‚  β€’ +2/3 Prevote κ²°κ³Ό 기반 νˆ¬ν‘œ                                               β”‚   β”‚
β”‚  β”‚  β€’ ExtendVote둜 Vote Extension μΆ”κ°€ (v0.38+)                                 β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                   β”‚ νˆ¬ν‘œ μˆ˜μ§‘                                       β”‚
β”‚                                   β–Ό                                                β”‚
β”‚            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚            β”‚          +2/3 Precommits μˆ˜μ§‘ μ™„λ£Œ?                   β”‚                 β”‚
β”‚            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚                    β”‚                β”‚                  β”‚                            β”‚
β”‚         +2/3 Block A         +2/3 nil         +2/3 미달성                          β”‚
β”‚                    β”‚                β”‚                  β”‚                            β”‚
β”‚                    β–Ό                β–Ό                  β–Ό                            β”‚
β”‚               COMMIT!          λ‹€μŒ λΌμš΄λ“œ        PrecommitWait                      β”‚
β”‚                    β”‚           (Round R+1)        (νƒ€μž„μ•„μ›ƒ)                         β”‚
β”‚                    β”‚                                   β”‚                            β”‚
β”‚                    β”‚                                   β–Ό                            β”‚
β”‚                    β”‚                             λ‹€μŒ λΌμš΄λ“œ                         β”‚
β”‚                    β”‚                             (Round R+1)                        β”‚
β”‚                    β–Ό                                                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  COMMIT                                                                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  1. 블둝 μ €μž₯ (BlockStore)                                                          β”‚
β”‚  2. FinalizeBlock 호좜 β†’ νŠΈλžœμž­μ…˜ μ‹€ν–‰                                              β”‚
β”‚  3. Commit 호좜 β†’ μƒνƒœ 영ꡬ μ €μž₯                                                     β”‚
β”‚  4. Mempool μ—…λ°μ΄νŠΈ (μ‹€ν–‰λœ TX 제거)                                                β”‚
β”‚  5. Height H+1둜 이동                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

5. ABCI 2.0 (ABCI++)

ABCI 2.0은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ»¨μ„Όμ„œμŠ€μ— 더 깊이 κ΄€μ—¬ν•  수 있게 ν•©λ‹ˆλ‹€.

PrepareProposal / ProcessProposal

PrepareProposal ProcessProposal
호좜 μ‹œμ  μ œμ•ˆμžκ°€ 블둝을 λ§Œλ“€ λ•Œ μ œμ•ˆλ°›μ€ 블둝 검증 μ‹œ
μ—­ν•  TX μž¬μ •λ ¬/μΆ”κ°€/제거
수수료 기반 μ •λ ¬
배치 μ΅œμ ν™”
블둝 μœ νš¨μ„± 검증
Accept/Reject κ²°μ •
μ¦‰μ‹œ μ‹€ν–‰ (μ˜΅μ…˜)
결정둠적? μ•„λ‹ˆμ˜€ (비결정적 ν—ˆμš©) 예 (λ°˜λ“œμ‹œ 결정둠적)

PrepareProposal μ˜ˆμ‹œ

abci/types/types.pb.go

type RequestPrepareProposal struct {
    MaxTxBytes           int64                  // μ΅œλŒ€ TX λ°”μ΄νŠΈ
    Txs                  [][]byte              // Mempool의 TXλ“€
    LocalLastCommit      ExtendedCommitInfo    // 이전 컀밋 + Vote Extensions
    Misbehavior          []Misbehavior         // μ•…μ„± ν–‰μœ„ 증거
    Height               int64
    Time                 time.Time
    NextValidatorsHash   []byte
    ProposerAddress      []byte
}

type ResponsePrepareProposal struct {
    Txs [][]byte  // μˆ˜μ •λœ TX λͺ©λ‘ (μž¬μ •λ ¬, μΆ”κ°€, 제거 κ°€λŠ₯)
}

// μ˜ˆμ‹œ: 수수료 기반 μ •λ ¬
func (app *MyApp) PrepareProposal(
    req *RequestPrepareProposal,
) *ResponsePrepareProposal {
    txs := req.Txs
    
    // 수수료둜 μ •λ ¬
    sort.Slice(txs, func(i, j int) bool {
        feeI := extractFee(txs[i])
        feeJ := extractFee(txs[j])
        return feeI > feeJ  // 높은 수수료 μš°μ„ 
    })

    // Vote Extension 데이터λ₯Ό "νŠΉλ³„ TX"둜 μ£Όμž… κ°€λŠ₯
    if len(req.LocalLastCommit.Votes) > 0 {
        oracleData := aggregateVoteExtensions(req.LocalLastCommit)
        txs = append([][]byte{oracleData}, txs...)
    }

    return &ResponsePrepareProposal{Txs: txs}
}

Vote Extensions (νˆ¬ν‘œ ν™•μž₯)

Vote Extensionsλž€?

κ²€μ¦μžκ°€ Precommit νˆ¬ν‘œμ— μΆ”κ°€ 데이터λ₯Ό 첨뢀할 수 μžˆλŠ” κΈ°λŠ₯μž…λ‹ˆλ‹€.

μ‚¬μš© 사둀: 였라클 가격 ν”Όλ“œ, μ•”ν˜Έν™”λœ λ©€ν’€, μž„κ³„κ°’ μ„œλͺ… λ“±

Vote Extension 흐름

Height H:
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Precommit 단계                                           β”‚
  β”‚                                                           β”‚
  β”‚  Validator A: Precommit + VoteExtension(price: $100)      β”‚
  β”‚  Validator B: Precommit + VoteExtension(price: $101)      β”‚
  β”‚  Validator C: Precommit + VoteExtension(price: $99)       β”‚
  β”‚                                                           β”‚
  β”‚  β†’ 블둝 컀밋 (Extensions도 ν•¨κ»˜ μ €μž₯)                      β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
Height H+1:
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  PrepareProposal (μ œμ•ˆμžλ§Œ)                                β”‚
  β”‚                                                           β”‚
  β”‚  LocalLastCommitμ—μ„œ Extensions μ ‘κ·Ό:                      β”‚
  β”‚  β€’ Validator A: $100                                      β”‚
  β”‚  β€’ Validator B: $101                                      β”‚
  β”‚  β€’ Validator C: $99                                       β”‚
  β”‚                                                           β”‚
  β”‚  쀑앙값 계산: $100 β†’ 블둝에 "νŠΉλ³„ TX"둜 포함                β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

μ½”λ“œ μ˜ˆμ‹œ

// ExtendVote: Precommit μ‹œ Vote Extension 생성
type RequestExtendVote struct {
    Hash             []byte   // 블둝 ν•΄μ‹œ
    Height           int64
    Time             time.Time
    Txs              [][]byte
    ProposedLastCommit CommitInfo
    Misbehavior      []Misbehavior
    NextValidatorsHash []byte
    ProposerAddress  []byte
}

type ResponseExtendVote struct {
    VoteExtension []byte  // νˆ¬ν‘œμ— 첨뢀할 데이터
}

// μ˜ˆμ‹œ: 였라클 가격 ν”Όλ“œ
func (app *OracleApp) ExtendVote(req *RequestExtendVote) *ResponseExtendVote {
    // μ™ΈλΆ€ APIμ—μ„œ 가격 쑰회 (비결정둠적 OK)
    price := fetchPriceFromAPI("ETH/USD")
    
    ext := &OraclePriceExtension{
        Price:     price,
        Timestamp: time.Now(),
    }
    
    extBytes, _ := proto.Marshal(ext)
    return &ResponseExtendVote{VoteExtension: extBytes}
}

// VerifyVoteExtension: λ‹€λ₯Έ κ²€μ¦μžμ˜ Extension 검증
func (app *OracleApp) VerifyVoteExtension(
    req *RequestVerifyVoteExtension,
) *ResponseVerifyVoteExtension {
    var ext OraclePriceExtension
    if err := proto.Unmarshal(req.VoteExtension, &ext); err != nil {
        return &ResponseVerifyVoteExtension{
            Status: ResponseVerifyVoteExtension_REJECT,
        }
    }
    
    // 가격이 합리적인 λ²”μœ„μΈμ§€ 확인 (결정둠적이어야 함)
    if ext.Price < 0 || ext.Price > 1000000 {
        return &ResponseVerifyVoteExtension{
            Status: ResponseVerifyVoteExtension_REJECT,
        }
    }
    
    return &ResponseVerifyVoteExtension{
        Status: ResponseVerifyVoteExtension_ACCEPT,
    }
}

μ£Όμ˜μ‚¬ν•­

  • 크기 μ œν•œ: λ„ˆλ¬΄ 크면 λ ˆμ΄ν„΄μ‹œ 증가
  • VerifyVoteExtension은 결정둠적: λͺ¨λ“  λ…Έλ“œκ°€ 같은 κ²°κ³Ό
  • λ„ˆλ¬΄ λ§Žμ€ REJECTλŠ” Liveness μ €ν•˜
  • Vote Extension은 블둝 크기에 영ν–₯: κ²€μ¦μžλ‹Ή μΆ”κ°€ 데이터 λ°œμƒ
  • Extension ν¬κΈ°λŠ” ν•©λ¦¬μ μœΌλ‘œ μœ μ§€ (ꢌμž₯: 수 KB 이내)
  • λ„€νŠΈμ›Œν¬ λŒ€μ—­ν­ κ³ λ € ν•„μˆ˜

FinalizeBlock

v0.38+μ—μ„œ BeginBlock + DeliverTx[] + EndBlock이 FinalizeBlock ν•˜λ‚˜λ‘œ ν†΅ν•©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

톡합 이유

  • API λ‹¨μˆœν™”: 3번 호좜 β†’ 1번 호좜
  • μ„±λŠ₯ κ°œμ„ : λ„€νŠΈμ›Œν¬ 왕볡 횟수 κ°μ†Œ
  • μ›μžμ„±: 블둝 싀행이 단일 νŠΈλžœμž­μ…˜μœΌλ‘œ 처리
  • μœ μ—°μ„±: μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ TX μ‹€ν–‰ μˆœμ„œλ₯Ό 더 잘 μ œμ–΄ κ°€λŠ₯

FinalizeBlock ꡬ쑰

type RequestFinalizeBlock struct {
    Txs                  [][]byte           // μ‹€ν–‰ν•  νŠΈλžœμž­μ…˜λ“€
    DecidedLastCommit    CommitInfo        // 이전 높이 컀밋 정보
    Misbehavior          []Misbehavior      // μ•…μ„± ν–‰μœ„ 증거 (이쀑 μ„œλͺ… λ“±)
    Hash                 []byte             // 블둝 ν•΄μ‹œ (κ²€μ¦μš©)
    Height               int64              // 블둝 높이
    Time                 time.Time          // 블둝 νƒ€μž„μŠ€νƒ¬ν”„
    NextValidatorsHash   []byte            // λ‹€μŒ κ²€μ¦μž μ„ΈνŠΈ ν•΄μ‹œ
    ProposerAddress      []byte            // μ œμ•ˆμž μ£Όμ†Œ
}

type ResponseFinalizeBlock struct {
    Events               []Event            // 블둝 레벨 이벀트
    TxResults            []ExecTxResult     // 각 TX μ‹€ν–‰ κ²°κ³Ό
    ValidatorUpdates     []ValidatorUpdate  // κ²€μ¦μž μ„ΈνŠΈ λ³€κ²½
    ConsensusParamUpdates *ConsensusParams  // μ»¨μ„Όμ„œμŠ€ νŒŒλΌλ―Έν„° λ³€κ²½
    AppHash              []byte             // μ•± μƒνƒœ ν•΄μ‹œ (Merkle Root)
}

type ExecTxResult struct {
    Code      uint32   // 0 = success, 1+ = error code
    Data      []byte   // TX μ‹€ν–‰ κ²°κ³Ό 데이터
    Log       string   // 둜그 λ©”μ‹œμ§€
    Info      string   // μΆ”κ°€ 정보
    GasWanted int64    // μš”μ²­ν•œ κ°€μŠ€
    GasUsed   int64    // μ‹€μ œ μ‚¬μš©ν•œ κ°€μŠ€
    Events    []Event  // TX 레벨 이벀트
    Codespace string   // μ—λŸ¬ μ½”λ“œ λ„€μž„μŠ€νŽ˜μ΄μŠ€
}

κ΅¬ν˜„ μ˜ˆμ‹œ

// μ˜ˆμ‹œ κ΅¬ν˜„
func (app *MyApp) FinalizeBlock(
    req *RequestFinalizeBlock,
) *ResponseFinalizeBlock {
    // 1. 블둝 μ‹œμž‘ 처리 (κΈ°μ‘΄ BeginBlock 둜직)
    app.logger.Info("FinalizeBlock", "height", req.Height, "txs", len(req.Txs))
    
    // 블둝 레벨 이벀트
    blockEvents := []Event{
        {
            Type: "begin_block",
            Attributes: []EventAttribute{
                {Key: "height", Value: fmt.Sprintf("%d", req.Height)},
                {Key: "proposer", Value: hex.EncodeToString(req.ProposerAddress)},
            },
        },
    }
    
    // 2. 각 νŠΈλžœμž­μ…˜ μ‹€ν–‰ (κΈ°μ‘΄ DeliverTx 둜직)
    txResults := make([]ExecTxResult, len(req.Txs))
    
    for i, txBytes := range req.Txs {
        // νŠΈλžœμž­μ…˜ λ””μ½”λ”©
        tx, err := app.decodeTx(txBytes)
        if err != nil {
            txResults[i] = ExecTxResult{
                Code: 1,
                Log:  fmt.Sprintf("failed to decode tx: %v", err),
            }
            continue
        }
        
        // νŠΈλžœμž­μ…˜ μ‹€ν–‰ (결정둠적이어야 함!)
        result := app.executeTx(tx)
        txResults[i] = result
        
        // μƒνƒœ μ—…λ°μ΄νŠΈ
        if result.Code == 0 {
            app.state.ApplyTx(tx)
        }
    }
    
    // 3. Evidence 처리 (μ•…μ„± κ²€μ¦μž μŠ¬λž˜μ‹±)
    for _, evidence := range req.Misbehavior {
        app.handleEvidence(evidence)
        
        blockEvents = append(blockEvents, Event{
            Type: "evidence",
            Attributes: []EventAttribute{
                {Key: "type", Value: evidence.Type.String()},
                {Key: "validator", Value: hex.EncodeToString(evidence.Validator.Address)},
                {Key: "height", Value: fmt.Sprintf("%d", evidence.Height)},
            },
        })
    }
    
    // 4. 블둝 μ’…λ£Œ 처리 (κΈ°μ‘΄ EndBlock 둜직)
    // 보상 λΆ„λ°°, μΈν”Œλ ˆμ΄μ…˜ λ“±
    app.distributeRewards(req.DecidedLastCommit)
    
    // κ²€μ¦μž μ„ΈνŠΈ μ—…λ°μ΄νŠΈ (μ˜΅μ…˜)
    var validatorUpdates []ValidatorUpdate
    if app.shouldUpdateValidators() {
        validatorUpdates = app.getValidatorUpdates()
    }
    
    // 5. μ•± ν•΄μ‹œ 계산
    appHash := app.state.ComputeHash()
    
    return &ResponseFinalizeBlock{
        Events:           blockEvents,
        TxResults:        txResults,
        ValidatorUpdates: validatorUpdates,
        AppHash:          appHash,
    }
}

κΈ°μ‘΄ λ²„μ „κ³Όμ˜ 비ꡐ

v0.34 (Legacy):

BeginBlock(height=100)
  β†’ 블둝 레벨 이벀트, 보상 λ“±
DeliverTx(tx1)
  β†’ TX μ‹€ν–‰
DeliverTx(tx2)
  β†’ TX μ‹€ν–‰
...
EndBlock(height=100)
  β†’ κ²€μ¦μž μ—…λ°μ΄νŠΈ, μ»¨μ„Όμ„œμŠ€ νŒŒλΌλ―Έν„° λ³€κ²½
Commit()
  β†’ AppHash λ°˜ν™˜

v0.38+ (ABCI 2.0):

FinalizeBlock(height=100, txs=[tx1, tx2, ...])
  β†’ λͺ¨λ“  λ‘œμ§μ„ ν•œ λ²ˆμ— 처리
  β†’ AppHash ν¬ν•¨ν•˜μ—¬ λ°˜ν™˜
Commit()
  β†’ μƒνƒœλ₯Ό λ””μŠ€ν¬μ— 영ꡬ μ €μž₯ (AppHashλŠ” FinalizeBlockμ—μ„œ 이미 λ°˜ν™˜)

μž₯점

  1. μ„±λŠ₯: gRPC 호좜 횟수 κ°μ†Œ (n+2 β†’ 2)
  2. μ›μžμ„±: 블둝 싀행이 단일 μž‘μ—…μœΌλ‘œ 처리
  3. λ‹¨μˆœμ„±: μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œκ°€ 더 κ°„κ²°
  4. μœ μ—°μ„±: TX κ°„ μ˜μ‘΄μ„± μ²˜λ¦¬κ°€ 더 쉬움

6. WAL (Write-Ahead Log)

WAL은 ν¬λž˜μ‹œ 볡ꡬλ₯Ό μœ„ν•œ 핡심 λ©”μ»€λ‹ˆμ¦˜μž…λ‹ˆλ‹€. λͺ¨λ“  μ»¨μ„Όμ„œμŠ€ λ©”μ‹œμ§€κ°€ 처리되기 전에 λ””μŠ€ν¬μ— κΈ°λ‘λ©λ‹ˆλ‹€.

consensus/wal.go

// WAL λ©”μ‹œμ§€ νƒ€μž…
type WALMessage interface{}

type msgInfo struct {
    Msg    Message
    PeerID p2p.ID
}

type timeoutInfo struct {
    Duration time.Duration
    Height   int64
    Round    int32
    Step     RoundStepType
}

type EndHeightMessage struct {
    Height int64
}

// WAL μΈν„°νŽ˜μ΄μŠ€
type WAL interface {
    Write(WALMessage) error      // 비동기 μ“°κΈ°
    WriteSync(WALMessage) error  // 동기 μ“°κΈ° (fsync)
    FlushAndSync() error         // 버퍼 ν”ŒλŸ¬μ‹œ
    
    SearchForEndHeight(height int64, options *WALSearchOptions) (
        *WALReader, bool, error)  // 볡ꡬ용 검색
}

WAL 볡ꡬ κ³Όμ •

1. λ…Έλ“œ ν¬λž˜μ‹œ λ°œμƒ
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Height 100, Round 0, Step Precommit    β”‚
   β”‚  λ‚΄ Precommit νˆ¬ν‘œ 전솑 직후 ν¬λž˜μ‹œ       β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. λ…Έλ“œ μž¬μ‹œμž‘
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  WAL 파일 읽기:                          β”‚
   β”‚  - msgInfo (Proposal)                   β”‚
   β”‚  - msgInfo (Prevote from peer1)         β”‚
   β”‚  - msgInfo (λ‚΄ Prevote) ← WriteSync     β”‚
   β”‚  - msgInfo (Precommit from peer2)       β”‚
   β”‚  - msgInfo (λ‚΄ Precommit) ← WriteSync   β”‚
   β”‚                                         β”‚
   β”‚  EndHeightMessage μ—†μŒ = 아직 컀밋 μ•ˆλ¨  β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3. μƒνƒœ 볡ꡬ
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  WAL λ©”μ‹œμ§€λ“€μ„ μˆœμ„œλŒ€λ‘œ μž¬μ‹€ν–‰           β”‚
   β”‚  β†’ ν¬λž˜μ‹œ 직전 μƒνƒœλ‘œ 볡ꡬ                β”‚
   β”‚  β†’ μ»¨μ„Όμ„œμŠ€ 계속 μ§„ν–‰                    β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Write vs WriteSync 차이점

  • Write: 버퍼에 μ“°κΈ° (빠름, ν”Όμ–΄ λ©”μ‹œμ§€μš©)

    • λ©”λͺ¨λ¦¬ λ²„νΌμ—λ§Œ 기둝
    • λ‚˜μ€‘μ— 일괄 fsync (배치 처리)
    • μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μœ„ν•΄ μ‚¬μš©
    • Proposal, Vote μˆ˜μ‹  등에 μ‚¬μš©
  • WriteSync: λ””μŠ€ν¬μ— fsync (느림, μžμ‹ μ˜ νˆ¬ν‘œμš©)

    • μ¦‰μ‹œ λ””μŠ€ν¬μ— flush
    • 운영체제 μΊμ‹œλ₯Ό κ±°μΉ˜μ§€ μ•Šκ³  물리 λ””μŠ€ν¬κΉŒμ§€ 기둝
    • ν¬λž˜μ‹œ μ‹œμ—λ„ 데이터 보μž₯
    • μžμ‹ μ˜ νˆ¬ν‘œλŠ” λ°˜λ“œμ‹œ WriteSync μ‚¬μš©
    • 이쀑 νˆ¬ν‘œ λ°©μ§€λ₯Ό μœ„ν•œ ν•„μˆ˜ λ©”μ»€λ‹ˆμ¦˜

μ™œ μžμ‹ μ˜ νˆ¬ν‘œλ§Œ WriteSync?

μžμ‹ μ˜ νˆ¬ν‘œλŠ” λ‹€μ‹œ 생성할 수 μ—†κ³ , λ§Œμ•½ λ‹€λ₯Έ νˆ¬ν‘œλ₯Ό μž¬μ „μ†‘ν•˜λ©΄ 이쀑 μ„œλͺ…(equivocation)이 λ©λ‹ˆλ‹€. λ”°λΌμ„œ ν¬λž˜μ‹œ 전에 λ””μŠ€ν¬μ— κΈ°λ‘λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.


7. Proposer μ„ μ • μ•Œκ³ λ¦¬μ¦˜

CometBFTλŠ” 가쀑 λΌμš΄λ“œ 둜빈(Weighted Round Robin) μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.

types/validator_set.go

// ValidatorSet은 κ²€μ¦μž μ§‘ν•©κ³Ό μ œμ•ˆμž 선정을 관리
type ValidatorSet struct {
    Validators []*Validator
    Proposer   *Validator
    
    totalVotingPower int64
}

type Validator struct {
    Address          Address
    PubKey           crypto.PubKey
    VotingPower      int64   // νˆ¬ν‘œλ ₯ (μŠ€ν…Œμ΄ν‚ΉλŸ‰)
    ProposerPriority int64   // ν˜„μž¬ μš°μ„ μˆœμœ„
}

// μ œμ•ˆμž μ„ μ •: 가쀑 λΌμš΄λ“œ 둜빈
func (vals *ValidatorSet) IncrementProposerPriority(times int32) {
    for i := int32(0); i < times; i++ {
        // 1. λͺ¨λ“  κ²€μ¦μžμ˜ μš°μ„ μˆœμœ„μ— VotingPower μΆ”κ°€
        for _, val := range vals.Validators {
            val.ProposerPriority += val.VotingPower
        }
        
        // 2. κ°€μž₯ 높은 μš°μ„ μˆœμœ„ κ²€μ¦μžκ°€ μ œμ•ˆμž
        vals.Proposer = vals.findProposer()
        
        // 3. μ œμ•ˆμžμ˜ μš°μ„ μˆœμœ„μ—μ„œ TotalVotingPower 차감
        vals.Proposer.ProposerPriority -= vals.TotalVotingPower()
    }
}

func (vals *ValidatorSet) findProposer() *Validator {
    var proposer *Validator
    for _, val := range vals.Validators {
        if proposer == nil || val.ProposerPriority > proposer.ProposerPriority {
            proposer = val
        }
    }
    return proposer
}

μ˜ˆμ‹œ

3 κ²€μ¦μž (νˆ¬ν‘œλ ₯: A=4, B=3, C=2, 총합=9)

초기 μƒνƒœ: Priority = [0, 0, 0]

Round 0:
  +VotingPower: [4, 3, 2]
  Proposer: A (졜고 μš°μ„ μˆœμœ„)
  -Total: [4-9, 3, 2] = [-5, 3, 2]

Round 1:
  +VotingPower: [-5+4, 3+3, 2+2] = [-1, 6, 4]
  Proposer: B (졜고 μš°μ„ μˆœμœ„)
  -Total: [-1, 6-9, 4] = [-1, -3, 4]

Round 2:
  +VotingPower: [-1+4, -3+3, 4+2] = [3, 0, 6]
  Proposer: C (졜고 μš°μ„ μˆœμœ„)
  -Total: [3, 0, 6-9] = [3, 0, -3]

Round 3:
  +VotingPower: [3+4, 0+3, -3+2] = [7, 3, -1]
  Proposer: A (졜고 μš°μ„ μˆœμœ„)
  ...

β†’ νˆ¬ν‘œλ ₯에 λΉ„λ‘€ν•˜μ—¬ μ œμ•ˆ 기회 λΆ„λ°°!
β†’ 결정둠적: λͺ¨λ“  λ…Έλ“œκ°€ 같은 μ œμ•ˆμž 계산

8. νƒ€μž„μ•„μ›ƒ μ„€μ •

config/config.go - ConsensusConfig

type ConsensusConfig struct {
    // Propose νƒ€μž„μ•„μ›ƒ (블둝 μ œμ•ˆ λŒ€κΈ°)
    TimeoutPropose        time.Duration  // κΈ°λ³Έ: 3s
    TimeoutProposeDelta   time.Duration  // κΈ°λ³Έ: 500ms (λΌμš΄λ“œλ‹Ή 증가)
    
    // Prevote νƒ€μž„μ•„μ›ƒ (+2/3 Any ν›„ λ‚˜λ¨Έμ§€ λŒ€κΈ°)
    TimeoutPrevote        time.Duration  // κΈ°λ³Έ: 1s
    TimeoutPrevoteDelta   time.Duration  // κΈ°λ³Έ: 500ms
    
    // Precommit νƒ€μž„μ•„μ›ƒ (+2/3 Any ν›„ λ‚˜λ¨Έμ§€ λŒ€κΈ°)
    TimeoutPrecommit      time.Duration  // κΈ°λ³Έ: 1s
    TimeoutPrecommitDelta time.Duration  // κΈ°λ³Έ: 500ms
    
    // Commit νƒ€μž„μ•„μ›ƒ (λ‹€μŒ 높이 μ „ λŒ€κΈ°)
    TimeoutCommit         time.Duration  // κΈ°λ³Έ: 1s
}

// λΌμš΄λ“œμ— λ”°λ₯Έ νƒ€μž„μ•„μ›ƒ 계산
func (cfg *ConsensusConfig) Propose(round int32) time.Duration {
    return cfg.TimeoutPropose + 
           cfg.TimeoutProposeDelta*time.Duration(round)
}

νƒ€μž„μ•„μ›ƒ μš”μ•½

νƒ€μž„μ•„μ›ƒ κΈ°λ³Έκ°’ μš©λ„ λ°œμƒ ν›„ λ™μž‘
TimeoutPropose 3s μ œμ•ˆ λŒ€κΈ° nil Prevote ν›„ λ‹€μŒ 단계
TimeoutPrevote 1s +2/3 Any ν›„ λ‚˜λ¨Έμ§€ λŒ€κΈ° Precommit λ‹¨κ³„λ‘œ
TimeoutPrecommit 1s +2/3 Any ν›„ λ‚˜λ¨Έμ§€ λŒ€κΈ° λ‹€μŒ λΌμš΄λ“œλ‘œ
TimeoutCommit 1s 컀밋 ν›„ λ‹€μŒ 높이 μ „ λŒ€κΈ° ν”Όμ–΄ 동기화 μ‹œκ°„

νƒ€μž„μ•„μ›ƒ νŠœλ‹ 팁

λΉ λ₯Έ 블둝 μ‹œκ°„ (Low Latency)

  • λͺ©ν‘œ: 1-3초 블둝 μ‹œκ°„
  • μ„€μ •:
    TimeoutPropose = 1s
    TimeoutPrevote = 500ms
    TimeoutPrecommit = 500ms
    TimeoutCommit = 500ms
    
  • 적용 ν™˜κ²½: 쒋은 λ„€νŠΈμ›Œν¬, 적은 κ²€μ¦μž (<50개)

느린 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ (Heavy Computation)

  • λͺ©ν‘œ: PrepareProposal/ProcessProposal μ‹œκ°„ 확보
  • μ„€μ •:
    TimeoutPropose = 10s  ← 증가
    TimeoutPrevote = 2s
    TimeoutPrecommit = 2s
    TimeoutCommit = 2s
    
  • 적용 ν™˜κ²½: λ³΅μž‘ν•œ TX 검증, λŒ€μš©λŸ‰ 블둝

λΆˆμ•ˆμ •ν•œ λ„€νŠΈμ›Œν¬ (High Latency)

  • λͺ©ν‘œ: λ„€νŠΈμ›Œν¬ μ§€μ—° 보상
  • μ„€μ •:
    TimeoutPropose = 5s
    TimeoutProposeDelta = 1s   ← Delta 증가
    TimeoutPrevote = 2s
    TimeoutPrevoteDelta = 1s   ← Delta 증가
    TimeoutPrecommit = 2s
    TimeoutPrecommitDelta = 1s ← Delta 증가
    TimeoutCommit = 3s
    
  • 적용 ν™˜κ²½: κΈ€λ‘œλ²Œ λΆ„μ‚° λ„€νŠΈμ›Œν¬, 높은 λ ˆμ΄ν„΄μ‹œ

λ§Žμ€ κ²€μ¦μž (Large Validator Set)

  • λͺ©ν‘œ: νˆ¬ν‘œ μˆ˜μ§‘ μ‹œκ°„ 확보
  • μ„€μ •:
    TimeoutPropose = 5s
    TimeoutPrevote = 3s        ← 증가
    TimeoutPrecommit = 3s      ← 증가
    TimeoutCommit = 2s
    
  • 적용 ν™˜κ²½: 100+ κ²€μ¦μž

μ‹€μ „ 팁

  1. λͺ¨λ‹ˆν„°λ§ ν•„μˆ˜: 각 단계별 μ‹€μ œ μ†Œμš” μ‹œκ°„ μΈ‘μ •
  2. 점진적 μ‘°μ •: μž‘μ€ λ³€κ²½ ν›„ 효과 κ΄€μ°°
  3. λΌμš΄λ“œ μ§„ν–‰ 확인: λΌμš΄λ“œκ°€ 자주 μ¦κ°€ν•˜λ©΄ νƒ€μž„μ•„μ›ƒ λΆ€μ‘± μ‹ ν˜Έ
  4. λ„€νŠΈμ›Œν¬ λͺ¨λ‹ˆν„°λ§: P2P λ©”μ‹œμ§€ μ „νŒŒ μ‹œκ°„ μΈ‘μ •
  5. Delta ν™œμš©: λΌμš΄λ“œκ°€ μ¦κ°€ν• μˆ˜λ‘ μžλ™μœΌλ‘œ νƒ€μž„μ•„μ›ƒ 증가

9. Evidence 처리

EvidenceλŠ” κ²€μ¦μžμ˜ μ•…μ„± ν–‰μœ„ (주둜 이쀑 μ„œλͺ…) μ¦κ±°μž…λ‹ˆλ‹€.

types/evidence.go

// Evidence μΈν„°νŽ˜μ΄μŠ€
type Evidence interface {
    Height() int64          // λ°œμƒ 높이
    Bytes() []byte         // 직렬화
    Hash() []byte          // ν•΄μ‹œ
    ValidateBasic() error  // κΈ°λ³Έ 검증
    String() string
}

// DuplicateVoteEvidence: 같은 (H,R)μ—μ„œ λ‹€λ₯Έ 블둝에 νˆ¬ν‘œ
type DuplicateVoteEvidence struct {
    VoteA            *Vote
    VoteB            *Vote
    TotalVotingPower int64
    ValidatorPower   int64
    Timestamp        time.Time
}

// LightClientAttackEvidence: 라이트 ν΄λΌμ΄μ–ΈνŠΈ 곡격
type LightClientAttackEvidence struct {
    ConflictingBlock *LightBlock
    CommonHeight     int64
}

이쀑 νˆ¬ν‘œ 감지

Height 100, Round 0:

κ²€μ¦μž Xκ°€ 두 개의 λ‹€λ₯Έ Prevote 전솑:

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  VoteA: Prevote for Block A                           β”‚
  β”‚    Height: 100, Round: 0                              β”‚
  β”‚    BlockID: 0xAAA...                                   β”‚
  β”‚    Signature: sig_A                                    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  VoteB: Prevote for Block B (λ‹€λ₯Έ 블둝!)              β”‚
  β”‚    Height: 100, Round: 0  (같은 H,R!)                 β”‚
  β”‚    BlockID: 0xBBB...                                   β”‚
  β”‚    Signature: sig_B                                    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

감지 λ…Έλ“œ:
  β†’ DuplicateVoteEvidence 생성
  β†’ Evidence Pool에 μΆ”κ°€
  β†’ λ‹€μŒ 블둝에 포함
  β†’ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μŠ¬λž˜μ‹± 처리

Evidence Pool

evidence/pool.go

// EvidencePool은 κ²€μ¦λœ Evidenceλ₯Ό 관리
type Pool struct {
    evidenceStore   dbm.DB
    evidenceList    *clist.CList      // λŒ€κΈ° 쀑인 Evidence
    
    state           sm.State
    stateDB         sm.Store
    blockStore      *store.BlockStore
}

// Evidence μΆ”κ°€
func (evpool *Pool) AddEvidence(ev types.Evidence) error {
    // 1. κΈ°λ³Έ 검증
    if err := ev.ValidateBasic(); err != nil {
        return err
    }
    
    // 2. 이미 μ‘΄μž¬ν•˜λŠ”μ§€ 확인
    if evpool.isPending(ev) {
        return ErrEvidenceAlreadyStored
    }
    
    // 3. 유효 κΈ°κ°„ 확인 (MaxAgeNumBlocks, MaxAgeDuration)
    if !evpool.isWithinMaxAge(ev) {
        return ErrEvidenceTooOld
    }
    
    // 4. κ²€μ¦μžκ°€ μ‹€μ œλ‘œ ν•΄λ‹Ή 높이에 μ‘΄μž¬ν–ˆλŠ”μ§€ 확인
    if err := evpool.verify(ev); err != nil {
        return err
    }
    
    // 5. Pool에 μΆ”κ°€
    evpool.evidenceList.PushBack(ev)
    return nil
}

10. 블둝 쑰각과 P2P

PartSet - 블둝 쑰각화

types/part_set.go

const (
    BlockPartSizeBytes = 65536  // 64KB
)

type PartSet struct {
    total uint32              // 총 쑰각 수
    hash  []byte              // Merkle Root
    
    mtx           sync.Mutex
    parts         []*Part
    partsBitArray *bits.BitArray  // μ–΄λ–€ 쑰각을 κ°€μ§€κ³  μžˆλŠ”μ§€
    count         uint32          // ν˜„μž¬ κ°€μ§„ 쑰각 수
}

type Part struct {
    Index uint32        // 쑰각 인덱슀
    Bytes []byte       // 쑰각 데이터
    Proof merkle.Proof // Merkle 증λͺ…
}

// 블둝을 쑰각으둜 λΆ„ν• 
func NewPartSetFromData(data []byte, partSize uint32) *PartSet {
    total := (uint32(len(data)) + partSize - 1) / partSize
    parts := make([]*Part, total)
    partsBytes := make([][]byte, total)
    
    for i := uint32(0); i < total; i++ {
        start := i * partSize
        end := min(uint32(len(data)), (i+1)*partSize)
        
        part := &Part{Index: i, Bytes: data[start:end]}
        parts[i] = part
        partsBytes[i] = part.Bytes
    }
    
    // Merkle Root 및 각 쑰각의 Proof 생성
    root, proofs := merkle.ProofsFromByteSlices(partsBytes)
    for i := 0; i < int(total); i++ {
        parts[i].Proof = *proofs[i]
    }
    
    return &PartSet{total: total, hash: root, parts: parts}
}

P2P λ™μ‹œμ„± - MConnection

p2p/conn/connection.go

// MConnection: λ©€ν‹°ν”Œλ ‰μŠ€ μ—°κ²°
type MConnection struct {
    conn        net.Conn
    bufConnReader *bufio.Reader
    bufConnWriter *bufio.Writer
    
    channels    []*Channel       // 채널별 큐
    channelsIdx map[byte]*Channel
    
    send        chan struct{}    // 전솑 μ‹ ν˜Έ
    pong        chan struct{}    // Pong 응닡
}

// 각 μ—°κ²°λ§ˆλ‹€ 2개 고루틴
func (c *MConnection) OnStart() error {
    go c.sendRoutine()  // 솑신 고루틴
    go c.recvRoutine()  // μˆ˜μ‹  고루틴
    return nil
}

// 솑신 루틴
func (c *MConnection) sendRoutine() {
    for {
        select {
        case <-c.send:
            // 각 μ±„λ„μ—μ„œ νŒ¨ν‚· κ°€μ Έμ™€μ„œ 전솑
            for _, ch := range c.channels {
                if ch.canSend() {
                    pkt := ch.nextPacket()
                    c.sendPacket(pkt)
                }
            }
        case <-c.quit:
            return
        }
    }
}

// μˆ˜μ‹  루틴
func (c *MConnection) recvRoutine() {
    for {
        pkt, err := c.recvPacket()
        if err != nil {
            c.stopForError(err)
            return
        }
        
        // 채널에 따라 λΆ„λ°°
        ch := c.channelsIdx[pkt.ChannelID]
        ch.recvPacket(pkt)
    }
}

P2P 채널 ꡬ쑰

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        MConnection                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                β”‚
β”‚  Channel 0x20 (StateChannel)                                   β”‚
β”‚  └─ μ»¨μ„Όμ„œμŠ€ μƒνƒœ λ©”μ‹œμ§€ (NewRoundStep, HasVote λ“±)             β”‚
β”‚                                                                β”‚
β”‚  Channel 0x21 (DataChannel)                                    β”‚
β”‚  └─ 블둝 쑰각 (BlockPart) 전솑                                 β”‚
β”‚                                                                β”‚
β”‚  Channel 0x22 (VoteChannel)                                    β”‚
β”‚  └─ νˆ¬ν‘œ λ©”μ‹œμ§€ (Prevote, Precommit)                           β”‚
β”‚                                                                β”‚
β”‚  Channel 0x23 (VoteSetBitsChannel)                             β”‚
β”‚  └─ νˆ¬ν‘œ λΉ„νŠΈλ§΅ (μ–΄λ–€ νˆ¬ν‘œλ₯Ό κ°€μ§€κ³  μžˆλŠ”μ§€)                     β”‚
β”‚                                                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

100개 ν”Όμ–΄ μ—°κ²° μ‹œ:
β€’ 200개 고루틴 (각 μ—°κ²°λ‹Ή send/recv 2개)
β€’ 각 채널은 λ…λ¦½μ μœΌλ‘œ 병렬 전솑

11. Mempool

mempool/clist_mempool.go

// CListMempool: κΈ°λ³Έ Mempool κ΅¬ν˜„ (μ—°κ²° 리슀트 기반)
type CListMempool struct {
    config *config.MempoolConfig
    
    proxyAppConn proxy.AppConnMempool  // ABCI μ—°κ²°
    
    txs          *clist.CList   // TX μ—°κ²° 리슀트 (μˆœμ„œ μœ μ§€)
    txsMap       map[TxKey]*clist.CElement  // λΉ λ₯Έ κ²€μƒ‰μš©
    txsBytes     int64          // 총 TX λ°”μ΄νŠΈ
    
    cache        TxCache        // 이미 λ³Έ TX μΊμ‹œ
    
    height       int64          // ν˜„μž¬ 높이
    notifiedTxsAvailable bool   // TX 있음 μ•Œλ¦Ό μ—¬λΆ€
}

// CheckTx: TX 검증 ν›„ Mempool에 μΆ”κ°€
func (mem *CListMempool) CheckTx(tx types.Tx) (*abciclient.ReqRes, error) {
    // 1. 크기 체크
    if len(tx) > mem.config.MaxTxBytes {
        return nil, ErrTxTooLarge
    }
    
    // 2. Mempool μš©λŸ‰ 체크
    if mem.txsBytes+int64(len(tx)) > mem.config.MaxTxsBytes {
        return nil, ErrMempoolIsFull
    }
    
    // 3. μΊμ‹œ 체크 (이미 λ³Έ TX?)
    if !mem.cache.Push(tx) {
        return nil, ErrTxInCache
    }
    
    // 4. ABCI CheckTx 호좜
    reqRes, err := mem.proxyAppConn.CheckTxAsync(
        context.TODO(),
        &abci.RequestCheckTx{Tx: tx},
    )
    
    return reqRes, err
}

// ReapMaxBytesMaxGas: 블둝에 포함할 TX κ°€μ Έμ˜€κΈ°
func (mem *CListMempool) ReapMaxBytesMaxGas(
    maxBytes, maxGas int64,
) types.Txs {
    mem.updateMtx.RLock()
    defer mem.updateMtx.RUnlock()
    
    var (
        totalBytes int64
        totalGas   int64
        txs        []types.Tx
    )
    
    for e := mem.txs.Front(); e != nil; e = e.Next() {
        memTx := e.Value.(*mempoolTx)
        
        // 크기/κ°€μŠ€ μ œν•œ 체크
        if totalBytes+int64(len(memTx.tx)) > maxBytes {
            break
        }
        if maxGas > 0 && totalGas+memTx.gasWanted > maxGas {
            continue
        }
        
        totalBytes += int64(len(memTx.tx))
        totalGas += memTx.gasWanted
        txs = append(txs, memTx.tx)
    }
    
    return txs
}

// Update: 블둝 μ‹€ν–‰ ν›„ Mempool μ—…λ°μ΄νŠΈ
func (mem *CListMempool) Update(
    height int64,
    txs types.Txs,
    txResults []*abci.ExecTxResult,
    preCheck PreCheckFunc,
    postCheck PostCheckFunc,
) error {
    mem.updateMtx.Lock()
    defer mem.updateMtx.Unlock()
    
    // μ‹€ν–‰λœ TX 제거
    for i, tx := range txs {
        if e, ok := mem.txsMap[TxKey(tx)]; ok {
            mem.removeTx(tx, e, false)
        }
    }
    
    mem.height = height
    
    // 남은 TX μž¬κ²€μ¦ (μ˜΅μ…˜)
    if mem.config.Recheck {
        mem.recheckTxs()
    }
    
    return nil
}

μš°μ„ μˆœμœ„ Mempool (v0.37+)

mempool/v1/mempool.go

// TxPriorityλŠ” TX의 μš°μ„ μˆœμœ„λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€
type TxPriority struct {
    Priority int64   // μš°μ„ μˆœμœ„ (λ†’μ„μˆ˜λ‘ λ¨Όμ €)
    Nonce    uint64  // 같은 λ°œμ‹ μžμ˜ TX μˆœμ„œ
    Sender   string  // λ°œμ‹ μž μ£Όμ†Œ
}

// μš°μ„ μˆœμœ„ 큐 기반 Mempool
type PriorityMempool struct {
    txs *TxPriorityQueue  // νž™ 기반 μš°μ„ μˆœμœ„ 큐
    // ...
}

// μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μš°μ„ μˆœμœ„ μ„€μ • (CheckTx 응닡)
type ResponseCheckTx struct {
    Code      uint32
    Data      []byte
    Log       string
    GasWanted int64
    GasUsed   int64
    Priority  int64   // ← μš°μ„ μˆœμœ„!
    Sender    string  // ← λ°œμ‹ μž
}

12. 정리

핡심 μ»΄ν¬λ„ŒνŠΈ μš”μ•½

μ»΄ν¬λ„ŒνŠΈ 파일 μœ„μΉ˜ μ—­ν•  핡심 포인트
State consensus/state.go μ»¨μ„Όμ„œμŠ€ μƒνƒœ λ¨Έμ‹  enterPropose, enterPrevote, enterPrecommit
VoteSet types/vote_set.go νˆ¬ν‘œ μˆ˜μ§‘ 및 집계 TwoThirdsMajority, HasTwoThirdsAny
WAL consensus/wal.go ν¬λž˜μ‹œ 볡ꡬ Write vs WriteSync
ABCI abci/types/ μ•± μΈν„°νŽ˜μ΄μŠ€ PrepareProposal, ProcessProposal, FinalizeBlock
Evidence evidence/pool.go μ•…μ„± ν–‰μœ„ 감지 DuplicateVoteEvidence
Mempool mempool/ TX λŒ€κΈ°μ—΄ CheckTx, ReapMaxBytesMaxGas, Update

ABCI 2.0 λ©”μ„œλ“œ 호좜 μˆœμ„œ

  1. InitChain - μ œλ„€μ‹œμŠ€ μ‹œ 1회 호좜. 초기 κ²€μ¦μž μ„€μ •.
  2. PrepareProposal (μ œμ•ˆμžλ§Œ) - 블둝 μ œμ•ˆ μ „. TX μž¬μ •λ ¬/μΆ”κ°€/제거 κ°€λŠ₯. 비결정둠적 OK.
  3. ProcessProposal (κ²€μ¦μž) - μ œμ•ˆλ°›μ€ 블둝 검증. Accept/Reject. 결정둠적 ν•„μˆ˜!
  4. ExtendVote (κ²€μ¦μž) - Precommit μ‹œ. Vote Extension 생성. 비결정둠적 OK.
  5. VerifyVoteExtension (κ²€μ¦μž) - λ‹€λ₯Έ κ²€μ¦μžμ˜ Extension 검증. 결정둠적 ν•„μˆ˜!
  6. FinalizeBlock - 블둝 ν™•μ • μ‹œ. TX μ‹€ν–‰. 결정둠적 ν•„μˆ˜!
  7. Commit - μƒνƒœ 영ꡬ μ €μž₯. AppHash λ°˜ν™˜.

Safety vs Liveness νŠΈλ ˆμ΄λ“œμ˜€ν”„

CometBFTλŠ” Safetyλ₯Ό μš°μ„ ν•©λ‹ˆλ‹€.

  • Safety: 1/3 μ΄μƒμ˜ μ•…μ„± λ…Έλ“œκ°€ μžˆμ–΄λ„ 포크 λΆˆκ°€λŠ₯
  • Liveness: 1/3 μ΄μƒμ˜ μ•…μ„± λ…Έλ“œκ°€ 체인을 멈좜 수 있음

포크보닀 멈좀이 λ‚«λ‹€λŠ” μ² ν•™μž…λ‹ˆλ‹€.

μ‹€μ „ 개발 κ°€μ΄λ“œ

1. ABCI μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 개발 μ‹œ μ£Όμ˜μ‚¬ν•­

결정둠적 μ‹€ν–‰ ν•„μˆ˜

// λ‚˜μœ 예: 비결정둠적
func (app *MyApp) ProcessProposal(req *RequestProcessProposal) *ResponseProcessProposal {
    // ν˜„μž¬ μ‹œκ°„ μ‚¬μš© - λ…Έλ“œλ§ˆλ‹€ λ‹€λ₯Ό 수 있음
    if time.Now().Unix() > deadline {
        return &ResponseProcessProposal{Status: ResponseProcessProposal_REJECT}
    }
    // ...
}

// 쒋은 예: 결정둠적
func (app *MyApp) ProcessProposal(req *RequestProcessProposal) *ResponseProcessProposal {
    // 블둝 νƒ€μž„μŠ€νƒ¬ν”„ μ‚¬μš© - λͺ¨λ“  λ…Έλ“œκ°€ κ°™μŒ
    if req.Time.Unix() > deadline {
        return &ResponseProcessProposal{Status: ResponseProcessProposal_REJECT}
    }
    // ...
}

μƒνƒœ λ¨Έμ‹  볡제

  • 같은 μž…λ ₯(블둝)이면 같은 좜λ ₯(AppHash)
  • λ‚œμˆ˜, μ‹œκ°„, μ™ΈλΆ€ API 호좜 κΈˆμ§€
  • λ§΅ 순회 μ‹œ ν‚€ μ •λ ¬ ν•„μˆ˜

2. 디버깅 방법

둜그 레벨 μ„€μ •

# config.toml
[log]
level = "consensus:debug,state:info,*:error"

μœ μš©ν•œ 둜그 λ©”μ‹œμ§€

  • enterNewRound: λΌμš΄λ“œ μ§„ν–‰ 좔적
  • addVote: νˆ¬ν‘œ μˆ˜μ§‘ 상황
  • finalizeCommit: 블둝 ν™•μ • 확인
  • applyBlock: 블둝 μ‹€ν–‰ μ‹œκ°„

λ©”νŠΈλ¦­ λͺ¨λ‹ˆν„°λ§

# Prometheus μ—”λ“œν¬μΈνŠΈ: http://localhost:26660/metrics

tendermint_consensus_height          # ν˜„μž¬ 블둝 높이
tendermint_consensus_rounds          # λΌμš΄λ“œ 횟수 (많으면 문제)
tendermint_consensus_validators      # κ²€μ¦μž 수
tendermint_mempool_size              # Mempool TX 수
tendermint_p2p_peers                 # μ—°κ²°λœ ν”Όμ–΄ 수

3. μ„±λŠ₯ μ΅œμ ν™”

Mempool μ„€μ •

[mempool]
size = 5000                    # Mempool 크기
cache_size = 10000            # TX μΊμ‹œ
max_tx_bytes = 1048576        # 1MB per TX
max_txs_bytes = 1073741824    # 1GB total

ν•©μ˜ μ„€μ •

[consensus]
timeout_propose = 3s
timeout_propose_delta = 500ms
timeout_prevote = 1s
timeout_prevote_delta = 500ms
timeout_precommit = 1s
timeout_precommit_delta = 500ms
timeout_commit = 1s

# λΉ λ₯Έ 블둝 (λ„€νŠΈμ›Œν¬ 쒋을 λ•Œ)
# timeout_commit = 100ms

P2P μ„€μ •

[p2p]
max_num_inbound_peers = 40
max_num_outbound_peers = 10
send_rate = 5120000           # 5MB/s
recv_rate = 5120000           # 5MB/s

4. 일반적인 문제 ν•΄κ²°

문제: λΌμš΄λ“œκ°€ 계속 증가

원인: νƒ€μž„μ•„μ›ƒ λΆ€μ‘±, λ„€νŠΈμ›Œν¬ μ§€μ—°, 느린 μ• ν”Œλ¦¬μΌ€μ΄μ…˜
ν•΄κ²°: 
  1. νƒ€μž„μ•„μ›ƒ 증가
  2. PrepareProposal/ProcessProposal μ΅œμ ν™”
  3. λ„€νŠΈμ›Œν¬ μƒνƒœ 점검

문제: 블둝 μ‹œκ°„μ΄ λΆˆκ·œμΉ™

원인: μ œμ•ˆμžκ°€ 자주 λ°”λ€œ, 일뢀 κ²€μ¦μž μ˜€ν”„λΌμΈ
ν•΄κ²°:
  1. κ²€μ¦μž λͺ¨λ‹ˆν„°λ§ κ°•ν™”
  2. TimeoutCommit μ‘°μ •
  3. ν”Όμ–΄ μ—°κ²° μƒνƒœ 확인

문제: Mempool이 가득 μ°Έ

원인: 블둝 크기 λΆ€μ‘±, TX 처리 속도 느림
ν•΄κ²°:
  1. ConsensusParams.Block.MaxBytes 증가
  2. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ TX 처리 μ΅œμ ν™”
  3. Mempool 크기 증가

문제: μƒνƒœ 동기화 μ‹€νŒ¨

원인: 블둝 μ €μž₯μ†Œ 손상, AppHash 뢈일치
ν•΄κ²°:
  1. unsafe-reset-all둜 μ΄ˆκΈ°ν™” (주의!)
  2. State sync μ‚¬μš©
  3. μŠ€λƒ…μƒ·μ—μ„œ 볡ꡬ

5. λ³΄μ•ˆ ꢌμž₯사항

ν‚€ 관리

  • κ²€μ¦μž ν‚€λŠ” ν•˜λ“œμ›¨μ–΄ λ³΄μ•ˆ λͺ¨λ“ˆ(HSM) μ‚¬μš©
  • priv_validator_key.json μ•”ν˜Έν™” μ €μž₯
  • 정기적인 ν‚€ λ‘œν…Œμ΄μ…˜

λ„€νŠΈμ›Œν¬ λ³΄μ•ˆ

  • μ„ΌνŠΈλ¦¬ λ…Έλ“œ μ•„ν‚€ν…μ²˜ μ‚¬μš©
  • κ²€μ¦μžλŠ” 곡개 IP λ…ΈμΆœ κΈˆμ§€
  • VPN λ˜λŠ” 프라이빗 λ„€νŠΈμ›Œν¬ μ‚¬μš©

λͺ¨λ‹ˆν„°λ§

  • 이쀑 μ„œλͺ… 감지 μ‹œμŠ€ν…œ
  • νˆ¬ν‘œ μ°Έμ—¬μœ¨ λͺ¨λ‹ˆν„°λ§
  • 블둝 생성 μ‹œκ°„ μ•ŒλžŒ

6. μ—…κ·Έλ ˆμ΄λ“œ μ „λž΅

μ†Œν”„νŠΈ 포크 (ν›„λ°© ν˜Έν™˜)

// μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 버전 체크
func (app *MyApp) ProcessProposal(req *RequestProcessProposal) *ResponseProcessProposal {
    if req.Height >= UPGRADE_HEIGHT {
        // μƒˆ 둜직
        return app.processProposalV2(req)
    }
    // 기쑴 둜직
    return app.processProposalV1(req)
}

ν•˜λ“œ 포크 (λΉ„ν˜Έν™˜ λ³€κ²½)

  1. κ±°λ²„λ„ŒμŠ€ μ œμ•ˆ 톡과
  2. μ—…κ·Έλ ˆμ΄λ“œ 높이 ν•©μ˜
  3. λͺ¨λ“  κ²€μ¦μž λ™μ‹œ μ—…κ·Έλ ˆμ΄λ“œ
  4. 높이 도달 μ‹œ μžλ™ μ „ν™˜

핡심 원칙 μš”μ•½

  1. 결정둠적 μ‹€ν–‰: 같은 블둝 = 같은 AppHash
  2. Safety First: ν¬ν¬λ³΄λ‹€λŠ” 멈좀
  3. 2/3 λ‹€μˆ˜κ²°: λΉ„μž”ν‹΄ ν—ˆμš© ν•œκ³„
  4. μ¦‰μ‹œ μ΅œμ’…μ„±: 컀밋 = 되돌릴 수 μ—†μŒ
  5. WAL ν™œμš©: ν¬λž˜μ‹œ 볡ꡬ 보μž₯

λ‹€μŒ 단계 ν•™μŠ΅

  • Cosmos SDK: IBC, Staking, Governance λͺ¨λ“ˆ
  • IBC: 크둜슀체인 톡신 ν”„λ‘œν† μ½œ
  • CosmWasm: 슀마트 μ»¨νŠΈλž™νŠΈ ν”„λ ˆμž„μ›Œν¬
  • Ignite CLI: 블둝체인 개발 도ꡬ

참고 자료


λ§ˆμ§€λ§‰ μ—…λ°μ΄νŠΈ: 2025λ…„ | κΈ°μ€€ 버전: CometBFT v0.38+ / v1.0

이 λ¬Έμ„œλŠ” Cosmos μƒνƒœκ³„ ν•™μŠ΅μ„ μœ„ν•΄ μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published