sequenceDiagram
autonumber
participant C as Client
participant L as Leader (Primary)
participant R1 as Replica 1
participant R2 as Replica 2
participant R3 as Replica 3
Note over C,R3: Phase 1: Request
C->>L: SubmitTx(tx, clientID)
Note over C,R3: Phase 2: Pre-Prepare
L->>L: proposeBlock()
L->>R1: PRE-PREPARE (view, seq, digest, block)
L->>R2: PRE-PREPARE
L->>R3: PRE-PREPARE
Note over C,R3: Phase 3: Prepare
R1->>L: PREPARE (view, seq, digest)
R1->>R2: PREPARE
R1->>R3: PREPARE
R2->>L: PREPARE
R2->>R1: PREPARE
R2->>R3: PREPARE
R3->>L: PREPARE
R3->>R1: PREPARE
R3->>R2: PREPARE
Note over C,R3: 2f+1 Prepares 수집 → PREPARED 상태
Note over C,R3: Phase 4: Commit
L->>R1: COMMIT (view, seq, digest)
L->>R2: COMMIT
L->>R3: COMMIT
R1->>L: COMMIT
R1->>R2: COMMIT
R1->>R3: COMMIT
R2->>L: COMMIT
R2->>R1: COMMIT
R2->>R3: COMMIT
R3->>L: COMMIT
R3->>R1: COMMIT
R3->>R2: COMMIT
Note over C,R3: 2f+1 Commits 수집 → COMMITTED 상태
Note over C,R3: Phase 5: Execute & Reply
L->>L: executeBlock()
R1->>R1: executeBlock()
R2->>R2: executeBlock()
R3->>R3: executeBlock()
L-->>C: Reply (result)
flowchart TB
subgraph Client["클라이언트"]
A[트랜잭션 생성]
end
subgraph Node["Node (node/node.go)"]
B[SubmitTx<br/>line:340]
C{mempool != nil?}
D[engine.SubmitRequest<br/>직접 전달]
end
subgraph Mempool["Mempool (mempool/mempool.go)"]
E[AddTxWithMeta<br/>line:269]
F[NewTxWithMeta<br/>Tx 객체 생성]
G[validateTx<br/>크기/중복 검증]
H{검증 통과?}
I[addTxLocked<br/>txStore에 저장]
J[newTxCh <- tx<br/>Reactor 알림]
K[에러 반환]
end
subgraph Reactor["Reactor (mempool/reactor.go)"]
L[broadcastLoop]
M[BroadcastTx<br/>P2P 전파]
end
subgraph Engine["Engine (consensus/pbft/engine.go)"]
N{IsPrimary?}
O[SubmitRequest<br/>requestChan에 추가]
P[proposeBlock 트리거]
Q[대기<br/>리더가 수집할 때까지]
end
A --> B
B --> C
C -->|No| D
C -->|Yes| E
E --> F
F --> G
G --> H
H -->|No| K
H -->|Yes| I
I --> J
J --> L
L --> M
I --> N
N -->|Yes| O
O --> P
N -->|No| Q
D --> O
style A fill:#e1f5fe
style P fill:#c8e6c9
style K fill:#ffcdd2
코드 경로 상세:
flowchart LR
subgraph "node/node.go:340-357"
A["SubmitTx(tx []byte, clientID string)"]
end
subgraph "mempool/mempool.go:269-344"
B["AddTxWithMeta()"]
B1["1. 크기 체크 (MaxTxBytes)"]
B2["2. Tx 생성 (NewTxWithMeta)"]
B3["3. 중복 체크 (txStore)"]
B4["4. 최근 제거 체크"]
B5["5. 가스 가격 체크"]
B6["6. Nonce 체크"]
B7["7. CheckTx 콜백"]
B8["8. 용량 체크/퇴출"]
B9["9. 저장 (addTxLocked)"]
B10["10. 알림 (newTxCh)"]
end
A --> B
B --> B1 --> B2 --> B3 --> B4 --> B5 --> B6 --> B7 --> B8 --> B9 --> B10
flowchart TB
subgraph Engine["Engine - 리더 (consensus/pbft/engine.go)"]
A[run 루프<br/>line:229]
B[requestChan에서<br/>요청 수신]
C{IsPrimary?}
D[proposeBlock<br/>line:310]
end
subgraph ProposeBlock["proposeBlock 상세"]
E[sequenceNum++<br/>블록 높이 증가]
F{mempool != nil?}
G[ReapMaxTxs 500<br/>FIFO로 수집]
H[app.GetPendingTxs<br/>하위 호환성]
I[요청 tx 추가]
J[CreateBlock<br/>블록 생성]
K[블록 해시 계산]
end
subgraph Broadcast["브로드캐스트"]
L[PrePrepareMsg 생성]
M[transport.Broadcast]
N[모든 Replica에게 전송]
end
subgraph StateLog["StateLog 저장"]
O[GetOrCreateState]
P[SetPrePrepare]
Q[Phase → PrePrepared]
end
A --> B
B --> C
C -->|No| A
C -->|Yes| D
D --> E
E --> F
F -->|Yes| G
F -->|No| H
G --> I
H --> I
I --> J
J --> K
K --> L
L --> M
M --> N
K --> O
O --> P
P --> Q
style D fill:#fff9c4
style G fill:#c8e6c9
style N fill:#bbdefb
Mempool에서 트랜잭션 수집:
sequenceDiagram
participant E as Engine
participant M as Mempool
participant T as types.Transaction
E->>M: ReapMaxTxs(500)
M->>M: txStore 순회
M->>M: Timestamp 기준 FIFO 정렬
M->>M: 최대 500개 선택
M-->>E: []*Tx 반환
loop 각 mempool Tx
E->>T: 변환
Note over E,T: mptx.ID → tx.ID<br/>mptx.Data → tx.Data<br/>mptx.Timestamp → tx.Timestamp<br/>mptx.Sender → tx.From
end
E->>E: CreateBlock(txs)
flowchart TB
subgraph Receive["메시지 수신"]
A[handleIncomingMessage<br/>line:251]
B[msgChan <- msg]
C[handleMessage<br/>line:262]
D{msg.Type?}
end
subgraph HandlePrePrepare["handlePrePrepare (line:392)"]
E[뷰 번호 확인]
F[메시지 디코딩<br/>JSON → PrePrepareMsg]
G[리더 검증<br/>PrimaryID == getPrimaryID]
H[다이제스트 검증<br/>block.Hash == msg.Digest]
I[stateLog.GetOrCreateState]
J[state.SetPrePrepare]
end
subgraph BroadcastPrepare["Prepare 브로드캐스트"]
K[PrepareMsg 생성]
L[View, SeqNum, Digest, NodeID]
M[transport.Broadcast]
end
subgraph HandlePrepare["handlePrepare (line:452)"]
N[뷰 번호 확인]
O[메시지 디코딩<br/>JSON → PrepareMsg]
P[state.AddPrepare]
Q{IsPrepared?<br/>2f+1 Prepares}
R[TransitionToPrepared]
S[Phase → Prepared]
end
A --> B --> C --> D
D -->|PrePrepare| E
E --> F --> G --> H --> I --> J --> K
K --> L --> M
D -->|Prepare| N
N --> O --> P --> Q
Q -->|Yes| R --> S
Q -->|No| P
style Q fill:#fff9c4
style S fill:#c8e6c9
Prepare 쿼럼 확인:
flowchart LR
subgraph "State.IsPrepared(quorum)"
A["PrepareCount()"]
B["len(prepares) >= quorum"]
C{">= 2f+1?"}
D["true"]
E["false"]
end
subgraph "QuorumSize 계산"
F["n = len(validators)"]
G["f = (n-1)/3"]
H["quorum = 2f+1"]
I["4노드: 2*1+1=3"]
end
A --> B --> C
C -->|Yes| D
C -->|No| E
F --> G --> H --> I
flowchart TB
subgraph AfterPrepared["PREPARED 상태 진입 후"]
A[TransitionToPrepared]
B[CommitMsg 생성]
C[View, SeqNum, Digest, NodeID]
D[transport.Broadcast]
end
subgraph HandleCommit["handleCommit (line:500)"]
E[뷰 번호 확인]
F[메시지 디코딩<br/>JSON → CommitMsg]
G[state.AddCommit]
H{IsCommitted?<br/>2f+1 Commits}
I[TransitionToCommitted]
J[Phase → Committed]
end
subgraph ExecuteBlock["executeBlock (line:539)"]
K{state.Executed?}
L[app.ExecuteBlock]
M[ABCI FinalizeBlock]
N[app.Commit]
O[상태 영구 저장]
P[state.MarkExecuted]
end
subgraph MempoolUpdate["Mempool 업데이트"]
Q{mempool != nil?}
R[committedTxs 수집]
S[mempool.Update]
T[txStore에서 제거]
U[recentlyRemoved에 추가]
V[메트릭 업데이트]
end
subgraph Finalize["완료"]
W[committedBlocks에 추가]
X[메트릭 기록]
Y[다음 라운드 준비]
end
A --> B --> C --> D
E --> F --> G --> H
H -->|No| G
H -->|Yes| I --> J --> K
K -->|Yes| W
K -->|No| L --> M --> N --> O --> P
P --> Q
Q -->|Yes| R --> S --> T --> U --> V --> W
Q -->|No| W
W --> X --> Y
style H fill:#fff9c4
style J fill:#c8e6c9
style S fill:#e1bee7
블록 실행 상세:
sequenceDiagram
participant E as Engine
participant A as ABCIAdapter
participant App as Cosmos SDK App
participant M as Mempool
E->>E: executeBlock(state)
Note over E,App: ABCI 2.0 FinalizeBlock
E->>A: ExecuteBlock(block)
A->>App: FinalizeBlock(req)
App->>App: BeginBlock 로직
loop 각 트랜잭션
App->>App: DeliverTx 실행
end
App->>App: EndBlock 로직
App-->>A: FinalizeBlockResponse
A-->>E: (appHash, txResults)
Note over E,App: ABCI Commit
E->>A: Commit()
A->>App: Commit()
App->>App: 상태 영구 저장
App-->>A: CommitResponse
A-->>E: appHash
Note over E,M: Mempool 정리
E->>M: Update(height, committedTxs)
M->>M: txStore에서 제거
M->>M: senderIndex 업데이트
M->>M: recentlyRemoved 추가
M-->>E: success
sequenceDiagram
autonumber
participant R1 as Replica 1
participant R2 as Replica 2
participant NL as New Leader
participant R3 as Replica 3
Note over R1,R3: 리더 타임아웃 발생
R1->>R1: onTimeout()
R1->>R1: initiateViewChange()
R1->>NL: VIEW-CHANGE (newView, lastSeq, checkpoints, preparedSet)
R1->>R2: VIEW-CHANGE
R1->>R3: VIEW-CHANGE
R2->>R2: onTimeout()
R2->>NL: VIEW-CHANGE
R2->>R1: VIEW-CHANGE
R2->>R3: VIEW-CHANGE
R3->>R3: onTimeout()
R3->>NL: VIEW-CHANGE
R3->>R1: VIEW-CHANGE
R3->>R2: VIEW-CHANGE
Note over NL: 2f+1 VIEW-CHANGE 수집
NL->>NL: sendNewView()
NL->>R1: NEW-VIEW (view, viewChangeMsgs, prePrepareMsgs)
NL->>R2: NEW-VIEW
NL->>R3: NEW-VIEW
Note over R1,R3: 새 뷰 진입, 미완료 요청 재처리
R1->>R1: handleNewView()
R2->>R2: handleNewView()
R3->>R3: handleNewView()
flowchart TB
subgraph Timeout["타임아웃 감지"]
A[viewChangeTimer 만료]
B[onTimeout]
C[initiateViewChange]
end
subgraph CreateViewChange["ViewChange 생성"]
D[newView = currentView + 1]
E[lastSeqNum 계산]
F[체크포인트 수집]
G[PreparedSet 수집]
H[ViewChangeMsg 생성]
end
subgraph Broadcast1["브로드캐스트"]
I[transport.Broadcast]
J[모든 노드에게 전송]
end
subgraph HandleViewChange["handleViewChange"]
K[메시지 검증]
L[viewChanges 맵에 추가]
M{2f+1 수집?}
N{I am new leader?}
end
subgraph SendNewView["새 리더: NewView 전송"]
O[ViewChange 메시지 모음]
P[미완료 PrePrepare 생성]
Q[NewViewMsg 생성]
R[broadcast NewView]
end
subgraph HandleNewView["handleNewView"]
S[2f+1 ViewChange 검증]
T[PrePrepare 검증]
U[currentView = newView]
V[상태 초기화]
W[미완료 요청 처리]
end
A --> B --> C --> D
D --> E --> F --> G --> H --> I --> J
J --> K --> L --> M
M -->|No| L
M -->|Yes| N
N -->|Yes| O --> P --> Q --> R
N -->|No| S
R --> S --> T --> U --> V --> W
style M fill:#fff9c4
style U fill:#c8e6c9
flowchart TB
subgraph Trigger["체크포인트 트리거"]
A[블록 커밋 완료]
B{seq % interval == 0?}
C[createCheckpoint]
end
subgraph Create["체크포인트 생성"]
D[현재 시퀀스 번호]
E[상태 다이제스트<br/>앱 해시]
F[CheckpointMsg 생성]
G[transport.Broadcast]
end
subgraph Handle["handleCheckpoint"]
H[다이제스트 검증]
I[checkpoints 맵에 추가]
J{2f+1 일치?}
K[markStable]
end
subgraph GC["가비지 컬렉션"]
L[lastStableSeq 업데이트]
M[garbageCollect]
N[오래된 stateLog 삭제]
O[메모리 해제]
end
A --> B
B -->|Yes| C --> D --> E --> F --> G
B -->|No| A
G --> H --> I --> J
J -->|No| I
J -->|Yes| K --> L --> M --> N --> O
style B fill:#fff9c4
style K fill:#c8e6c9
stateDiagram-v2
[*] --> IDLE: 초기 상태
IDLE --> PRE_PREPARED: PrePrepare 수신
PRE_PREPARED --> PREPARED: 2f Prepares
PREPARED --> COMMITTED: 2f+1 Commits
COMMITTED --> EXECUTED: executeBlock()
EXECUTED --> TX_REMOVED: Mempool.Update()
TX_REMOVED --> IDLE: 다음 라운드
state VIEW_CHANGE {
[*] --> VC_WAITING: 타임아웃
VC_WAITING --> VC_COLLECTED: 2f+1 ViewChange
VC_COLLECTED --> NEW_VIEW: NewView 수신
NEW_VIEW --> [*]: 뷰 전환 완료
}
IDLE --> VIEW_CHANGE: 타임아웃
PRE_PREPARED --> VIEW_CHANGE: 타임아웃
PREPARED --> VIEW_CHANGE: 타임아웃
VIEW_CHANGE --> IDLE: 새 뷰 진입
Phase 상세:
flowchart LR
subgraph Phases["PBFT Phase"]
A["IDLE<br/>(0)"]
B["PRE_PREPARED<br/>(1)"]
C["PREPARED<br/>(2)"]
D["COMMITTED<br/>(3)"]
E["EXECUTED<br/>(4)"]
end
A -->|"SetPrePrepare()"| B
B -->|"IsPrepared(2f+1)"| C
C -->|"IsCommitted(2f+1)"| D
D -->|"executeBlock()"| E
style A fill:#e0e0e0
style B fill:#fff9c4
style C fill:#c8e6c9
style D fill:#bbdefb
style E fill:#e1bee7
flowchart TB
subgraph External["외부"]
A[Client]
end
subgraph Node["Node"]
B[SubmitTx]
end
subgraph Mempool["Mempool"]
C[AddTxWithMeta]
D[txStore 저장]
E[senderIndex 업데이트]
end
subgraph Reactor["Reactor"]
F[newTxCh 수신]
G[broadcastLoop]
H[BroadcastTx]
end
subgraph P2P["P2P Network"]
I[다른 노드들]
end
subgraph Engine["Engine"]
J{IsPrimary?}
K[SubmitRequest]
L[requestChan]
end
A -->|"tx, clientID"| B
B --> C --> D --> E
E --> F --> G --> H --> I
D --> J
J -->|Yes| K --> L
J -->|No| M[대기]
style A fill:#e1f5fe
style D fill:#c8e6c9
style H fill:#bbdefb
flowchart TB
subgraph Engine["Engine (리더)"]
A[proposeBlock]
B[mempool.ReapMaxTxs 500]
end
subgraph Mempool["Mempool.ReapMaxTxs()"]
C[txStore 순회]
D[Timestamp 기준 정렬<br/>FIFO]
E["최대 max개 선택"]
F["[]*Tx 반환"]
end
subgraph Convert["트랜잭션 변환"]
G["mptx.ID → tx.ID"]
H["mptx.Data → tx.Data"]
I["mptx.Timestamp → tx.Timestamp"]
J["mptx.Sender → tx.From"]
end
subgraph Block["블록 생성"]
K[types.Transaction 목록]
L[CreateBlock]
M[블록 해시 계산]
end
A --> B --> C --> D --> E --> F
F --> G --> H --> I --> J --> K
K --> L --> M
style D fill:#fff9c4
style M fill:#c8e6c9
flowchart TB
subgraph Execute["executeBlock 완료"]
A[state.MarkExecuted]
B[committedBlocks 추가]
end
subgraph Cleanup["Mempool 정리"]
C{mempool != nil?}
D[committedTxs 수집]
E["tx.Data → [][]byte"]
F[mempool.Update]
end
subgraph Update["Mempool.Update()"]
G["txStore에서 삭제"]
H["senderIndex 업데이트"]
I["recentlyRemoved 추가"]
J["height 업데이트"]
K["메트릭 업데이트<br/>TxsCommitted++"]
end
subgraph Metrics["메트릭"]
L[RecordBlockExecutionTime]
M[IncrementConsensusRounds]
N[RecordConsensusDuration]
end
A --> B --> C
C -->|Yes| D --> E --> F --> G
G --> H --> I --> J --> K
C -->|No| L
K --> L --> M --> N
style F fill:#e1bee7
style G fill:#ffcdd2
flowchart TB
subgraph Network["네트워크 수신"]
A[gRPC Transport]
B[BroadcastMessage RPC]
C[메시지 디코딩]
end
subgraph Handler["메시지 핸들러"]
D[handleIncomingMessage]
E[msgChan <- msg]
F{채널 가득?}
G[메시지 버림]
end
subgraph Loop["run 루프"]
H[select]
I[msgChan에서 수신]
J[handleMessage]
end
subgraph Dispatch["타입별 분기"]
K{msg.Type}
L[handlePrePrepare]
M[handlePrepare]
N[handleCommit]
O[handleViewChange]
P[handleNewView]
end
subgraph Metrics["메트릭"]
Q[RecordMessageProcessingTime]
R[IncrementMessagesReceived]
end
A --> B --> C --> D --> E --> F
F -->|Yes| G
F -->|No| H
H --> I --> J --> K
K -->|PrePrepare| L
K -->|Prepare| M
K -->|Commit| N
K -->|ViewChange| O
K -->|NewView| P
L --> Q --> R
M --> Q
N --> Q
O --> Q
P --> Q
style K fill:#fff9c4
flowchart LR
subgraph Nodes["노드 구성 (n=4)"]
A["f = (n-1)/3 = 1"]
B["최대 1개 비잔틴 허용"]
end
subgraph Quorums["쿼럼 요구사항"]
C["Pre-Prepare: 1 (리더만)"]
D["Prepare: 2f = 2"]
E["Commit: 2f+1 = 3"]
F["ViewChange: 2f+1 = 3"]
G["Checkpoint: 2f+1 = 3"]
end
A --> C
A --> D
A --> E
A --> F
A --> G
| 단계 | 필요 메시지 수 | 4노드 | 7노드 | 10노드 |
|---|---|---|---|---|
| Pre-Prepare | 1 | 1 | 1 | 1 |
| Prepare | 2f | 2 | 4 | 6 |
| Commit | 2f+1 | 3 | 5 | 7 |
| View Change | 2f+1 | 3 | 5 | 7 |
| Checkpoint | 2f+1 | 3 | 5 | 7 |
flowchart TB
subgraph NodePkg["node/"]
A["SubmitTx()<br/>node.go:340"]
end
subgraph MempoolPkg["mempool/"]
B["AddTxWithMeta()<br/>mempool.go:269"]
C["ReapMaxTxs()<br/>mempool.go"]
D["Update()<br/>mempool.go"]
end
subgraph EnginePkg["consensus/pbft/"]
E["proposeBlock()<br/>engine.go:310"]
F["handlePrePrepare()<br/>engine.go:392"]
G["handlePrepare()<br/>engine.go:452"]
H["handleCommit()<br/>engine.go:500"]
I["executeBlock()<br/>engine.go:539"]
J["SetMempool()<br/>engine.go:211"]
end
subgraph TransportPkg["transport/"]
K["Broadcast()<br/>grpc.go"]
L["Send()<br/>grpc.go"]
end
A --> B
E --> C
I --> D
E --> K
F --> K
G --> K
H --> I
style A fill:#e1f5fe
style E fill:#fff9c4
style I fill:#c8e6c9
flowchart TB
subgraph Client["클라이언트"]
A[Transaction]
end
subgraph Node["PBFT Node"]
subgraph Components["컴포넌트"]
B[Node]
C[Engine]
D[Mempool]
E[Reactor]
F[Transport]
G[Metrics]
end
subgraph ABCI["ABCI 2.0"]
H[ABCIAdapter]
end
end
subgraph App["Cosmos SDK App"]
I[Application]
end
subgraph Network["P2P Network"]
J[Other Nodes]
end
subgraph Monitoring["모니터링"]
K[Prometheus]
end
A -->|SubmitTx| B
B --> D
B --> C
D --> E
E --> F
F <--> J
C --> H
H <--> I
C --> G
G --> K
style C fill:#fff9c4
style D fill:#c8e6c9
style H fill:#bbdefb
flowchart TB
subgraph Errors["에러 유형"]
A[ErrTxAlreadyExists<br/>중복 트랜잭션]
B[ErrMempoolFull<br/>Mempool 가득 참]
C[ErrTxTooLarge<br/>트랜잭션 너무 큼]
D[ErrInvalidTx<br/>유효하지 않은 tx]
E[ViewChange 필요<br/>리더 장애]
end
subgraph Handling["처리"]
F[트랜잭션 거부]
G[퇴출 후 재시도]
H[클라이언트 에러 반환]
I[View Change 시작]
end
A --> F --> H
B --> G
C --> F --> H
D --> F --> H
E --> I
style A fill:#ffcdd2
style B fill:#fff9c4
style I fill:#bbdefb
flowchart LR
subgraph Sources["메트릭 소스"]
A[Engine<br/>합의 라운드]
B[Mempool<br/>트랜잭션 수]
C[Transport<br/>메시지 수]
D[Block<br/>실행 시간]
end
subgraph Metrics["Prometheus 메트릭"]
E[consensus_rounds_total]
F[consensus_duration]
G[mempool_size]
H[messages_sent_total]
I[block_execution_time]
J[tps]
end
subgraph Export["내보내기"]
K[/metrics 엔드포인트]
L[Prometheus 스크래핑]
M[Grafana 대시보드]
end
A --> E
A --> F
B --> G
C --> H
D --> I
A --> J
E --> K
F --> K
G --> K
H --> K
I --> K
J --> K
K --> L --> M