Saltar al contenido principal

PBFT: Tolerancia a Fallos Bizantinos

En el universo de KodeChain, donde la confianza absoluta es fundamental, el algoritmo Practical Byzantine Fault Tolerance (PBFT) emerge como el guardián de la verdad para registros críticos. Esta guía revela cómo el código en consensus/pbft.go transforma principios matemáticos complejos en un sistema robusto de validación distribuida.

La Ciencia Detrás de PBFT

Tolerancia a Fallos Bizantinos

PBFT resuelve el problema más desafiante en sistemas distribuidos: ¿cómo mantener la consistencia cuando algunos participantes pueden comportarse de manera maliciosa o fallar arbitrariamente? A diferencia de otros algoritmos que requieren suposiciones simplistas sobre fallos, PBFT tolera hasta f nodos fallidos en un sistema de 3f+1 nodos totales.

// En pbft.go, línea 76
config := &PBFTConfig{
Timeout: 10 * time.Second,
CheckpointPeriod: 100,
WatermarkPeriod: 1000,
MaxFaultyNodes: 1, // Puede tolerar 1 nodo fallido
ViewChangeTimeout: 30 * time.Second,
}

Arquitectura del Sistema PBFT

El Núcleo: Estructura PBFT

type PBFT struct {
NodeID string
Primary string // Nodo líder actual
Validators []string // Lista de validadores
BlockStates map[string]*PBFTState // Estados por bloque
View uint64 // Número de vista actual
SequenceNumber uint64 // Número de secuencia
Broadcaster MessageBroadcaster
Signer security.Signer // Firmas post-cuánticas
// ... más campos
}

Cada instancia PBFT mantiene un estado completo del protocolo, incluyendo el seguimiento de mensajes de consenso para cada bloque.

Estados de Bloque

type PBFTState struct {
Block *blockchain.Block `json:"block"`
PrePrepared bool `json:"prePrepared"`
PrepareCount int `json:"prepareCount"`
CommitCount int `json:"commitCount"`
Prepared bool `json:"prepared"`
Committed bool `json:"committed"`
PreparedBy map[string]bool `json:"preparedBy"`
CommittedBy map[string]bool `json:"committedBy"`
ViewChangeCount int `json:"viewChangeCount"`
}

El Protocolo de Tres Fases

Fase 1: Pre-Prepare

El nodo primario inicia el consenso enviando un mensaje pre-prepare a todos los validadores:

func (p *PBFT) initiateConsensus(block *blockchain.Block) error {
p.SequenceNumber++

prePrepare := &PBFTPrePrepare{
Block: block,
View: p.View,
Digest: block.Hash,
}

if p.Broadcaster != nil {
if err := p.Broadcaster.Broadcast(prePrepare); err != nil {
return fmt.Errorf("error enviando pre-prepare: %v", err)
}
}

utils.LogInfo("PBFT: Consenso iniciado para bloque %s, secuencia %d", block.Hash, p.SequenceNumber)
return nil
}

Fase 2: Prepare

Los validadores verifican el bloque y envían mensajes prepare:

func (p *PBFT) handlePrepare(block *blockchain.Block, nodeID string) error {
state := p.GetBlockState(block)

p.mutex.Lock()
defer p.mutex.Unlock()

if state.PreparedBy[nodeID] {
return nil // Ya preparado por este nodo
}

state.PreparedBy[nodeID] = true
state.PrepareCount++

utils.LogInfo("Received prepare for block %s from node %s (%d/%d)",
block.Hash, nodeID, state.PrepareCount, len(p.Validators))

threshold := 2 * len(p.Validators) / 3
if state.PrepareCount >= threshold && !state.Prepared {
state.Prepared = true
utils.LogInfo("Block %s prepared with %d votes", block.Hash, state.PrepareCount)
return p.BroadcastCommit(block)
}

return nil
}

Fase 3: Commit

Una vez alcanzado el quórum de prepare, los validadores envían commit:

func (p *PBFT) handleCommit(block *blockchain.Block, nodeID string) error {
state := p.GetBlockState(block)

p.mutex.Lock()
defer p.mutex.Unlock()

if state.CommittedBy[nodeID] {
return nil // Ya comprometido por este nodo
}

state.CommittedBy[nodeID] = true
state.CommitCount++

utils.LogInfo("Received commit for block %s from node %s (%d/%d)",
block.Hash, nodeID, state.CommitCount, len(p.Validators))

threshold := 2 * len(p.Validators) / 3
if state.CommitCount >= threshold && !state.Committed {
state.Committed = true
utils.LogInfo("Block %s committed with %d votes", block.Hash, state.CommitCount)

if p.onBlockCommitted != nil {
p.onBlockCommitted(block)
}
}

return nil
}

Registros Críticos: El Caso de Uso Especializado

Arquitectura de Registros Críticos

PBFT en KodeChain está especializado para validar registros críticos no económicos:

type CriticalRecord struct {
ID string `json:"id"`
Type string `json:"type"` // DOCUMENT, AUDIT, LEGAL, etc.
DocumentHash string `json:"documentHash"`
Timestamp time.Time `json:"timestamp"`
ValidatorID string `json:"validatorId"`
Status string `json:"status"` // PENDING, COMMITTED, REJECTED
Data []byte `json:"data"`
Signature string `json:"signature"`
}

Validación de Registros

func (p *PBFT) ValidateCriticalRecord(record *CriticalRecord) bool {
// Rechazar registros económicos - PBFT solo maneja críticos
if record.Type == "ECONOMIC" {
utils.LogWarning("PBFT: Rejecting economic record - PBFT only handles critical records")
return false
}

// Validar estructura
if record.DocumentHash == "" || record.Timestamp.IsZero() {
utils.LogWarning("PBFT: Invalid record structure")
return false
}

// Verificar permisos por tipo
if !p.hasPermissionForRecordType(record.Type) {
utils.LogWarning("PBFT: Validator lacks permission for record type: %s", record.Type)
return false
}

// Validar firma
if !p.validateRecordSignature(record) {
utils.LogWarning("PBFT: Invalid record signature")
return false
}

return true
}

Tipos de Registro Permitidos

allowedTypes := []string{
"DOCUMENT", // Documentos legales
"AUDIT", // Auditorías
"LEGAL", // Registros legales
"CERTIFICATE", // Certificados
"PATENT", // Patentes
"TRADEMARK", // Marcas registradas
"CONTRACT", // Contratos
"COMPLIANCE", // Cumplimiento regulatorio
}

Minería Automática de Bloques

El Block Miner PBFT

type PBFTBlockMiner struct {
pbft *PBFT
mempool interface{}
blockchain interface{}
checkInterval time.Duration
isMining bool
minTransactions int
maxBlockTime time.Duration
lastBlockTime time.Time
}

Lógica de Minería

func (bm *PBFTBlockMiner) checkAndMineBlock() {
if !bm.pbft.IsRunning() {
return
}

transactions := bm.getPendingTransactions()
if len(transactions) == 0 {
return
}

shouldMine := bm.shouldCreateBlock(len(transactions))
if !shouldMine {
return
}

block, err := bm.createBlock(transactions)
if err != nil {
utils.LogError("PBFT Block Miner: Error creating block: %v", err)
return
}

if err := bm.startPBFTConsensus(block); err != nil {
utils.LogError("PBFT Block Miner: Error starting consensus: %v", err)
return
}
}

Gestión de Vistas y Recuperación

Cambios de Vista

Cuando el nodo primario falla, el sistema inicia un cambio de vista:

func (p *PBFT) InitiateViewChange() error {
p.mutex.Lock()
p.View++
newView := p.View
p.mutex.Unlock()

utils.LogInfo("Initiated view change to view %d", newView)
return nil
}

Procesamiento de Cambios de Vista

func (p *PBFT) handleViewChangeMessage(message *PBFTMessage) error {
viewChangeData, ok := message.Data.(*PBFTViewChange)
if !ok {
return fmt.Errorf("formato de mensaje view-change inválido")
}

if _, exists := p.viewChanges[message.NodeID]; !exists {
p.viewChanges[message.NodeID] = make(map[uint64]int)
}

p.viewChanges[message.NodeID][viewChangeData.NewView]++

viewCount := 0
for _, nodeViews := range p.viewChanges {
if count, exists := nodeViews[viewChangeData.NewView]; exists {
viewCount += count
}
}

threshold := 2 * len(p.Validators) / 3
if viewCount >= threshold && viewChangeData.NewView > p.View {
p.View = viewChangeData.NewView
if len(p.Validators) > 0 {
p.Primary = p.Validators[int(p.View)%len(p.Validators)]
}
utils.LogInfo("PBFT: Vista cambiada a %d. Nuevo primario: %s", p.View, p.Primary)
}

return nil
}

Integración con Staking

Fees Mensuales

PBFT incluye un sistema de fees mensuales para uso de registros críticos:

func (p *PBFT) ProcessMonthlyFee(validatorAddress string, feeAmount uint64) error {
if !slices.Contains(p.Validators, validatorAddress) {
return fmt.Errorf("validator not active")
}

if p.stakingContract != nil {
if staking, ok := p.stakingContract.(interface {
ProcessMonthlyFee(validatorAddress string, feeAmount uint64, serviceType string) error
}); ok {
return staking.ProcessMonthlyFee(validatorAddress, feeAmount, "PBFT_CRITICAL_RECORDS")
}
}

utils.LogInfo("PBFT: Monthly fee processed for validator %s: %d", validatorAddress, feeAmount)
return nil
}

Métricas y Monitoreo

Métricas del Sistema PBFT

type PBFTMetrics struct {
ViewNumber uint64
SequenceNumber uint64
Checkpoint uint64
Watermark uint64
TotalBlocks uint64
CommittedBlocks uint64
ViewChanges uint64
PrimaryChanges uint64
AverageLatency time.Duration
FaultyNodes uint64
}

Recolección de Métricas

func (p *PBFT) GetMetrics() *PBFTMetrics {
p.mutex.RLock()
defer p.mutex.RUnlock()

return &PBFTMetrics{
ViewNumber: p.View,
SequenceNumber: p.SequenceNumber,
Checkpoint: p.checkpoint,
Watermark: p.watermark,
TotalBlocks: uint64(len(p.BlockStates)),
CommittedBlocks: p.countCommittedBlocks(),
ViewChanges: p.countViewChanges(),
}
}

Seguridad Post-Cuántica

Firmas en PBFT

Todos los mensajes PBFT están firmados con ML-DSA-65:

func NewPBFT(broadcaster MessageBroadcaster, logger Logger) *PBFT {
qs, err := security.NewQuantumSigner()
if err != nil {
panic("No se pudo inicializar QuantumSigner: " + err.Error())
}

return &PBFT{
Signer: qs,
// ... otros campos
}
}

El Futuro de PBFT en KodeChain

PBFT representa la evolución de la tolerancia a fallos bizantinos, adaptada específicamente para el mundo post-cuántico. Su especialización en registros críticos, combinada con la robustez matemática del algoritmo original, crea un sistema que puede resistir no solo fallos técnicos, sino también ataques sofisticados.

Cada línea de código en consensus/pbft.go está diseñada para mantener la integridad de los registros más críticos de la sociedad digital, desde contratos legales hasta certificados oficiales, asegurando que la verdad prevalezca incluso en presencia de adversarios maliciosos.