@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
523 lines (449 loc) • 15 kB
text/typescript
/**
* Byzantine Fault-Tolerant Consensus System
* Implements PBFT (Practical Byzantine Fault Tolerance) algorithm
* Handles up to 33% malicious agents while maintaining correctness
*/
import { EventEmitter } from "events";
import { createHash } from "crypto";
export interface Agent {
id: string;
publicKey: string;
isLeader: boolean;
isMalicious?: boolean;
reputation: number;
lastActiveTime: Date;
}
export interface ConsensusMessage {
type: "pre-prepare" | "prepare" | "commit" | "view-change" | "new-view";
viewNumber: number;
sequenceNumber: number;
digest: string;
payload: any;
timestamp: Date;
signature: string;
senderId: string;
}
export interface ConsensusProposal {
id: string;
content: any;
proposerId: string;
timestamp: Date;
hash: string;
}
export interface ConsensusState {
currentView: number;
sequenceNumber: number;
phase: "pre-prepare" | "prepare" | "commit" | "view-change";
leader: string;
activeAgents: Set<string>;
proposals: Map<string, ConsensusProposal>;
messages: Map<string, ConsensusMessage[]>;
committed: Set<string>;
}
export class ByzantineConsensus extends EventEmitter {
private agents: Map<string, Agent> = new Map();
private state: ConsensusState;
private messageLog: ConsensusMessage[] = [];
private faultThreshold: number;
private minQuorum: number; // Byzantine: Math.floor(2*n/3)+1
private timeoutDuration: number = 30000; // 30 seconds
private viewChangeTimeout: ReturnType<typeof setTimeout> | null = null;
private performance: {
consensusRounds: number;
averageLatency: number;
faultsDetected: number;
successRate: number;
};
constructor(
private agentId: string,
private totalAgents: number = 4,
) {
super();
this.faultThreshold = Math.floor((totalAgents - 1) / 3);
this.minQuorum = Math.floor((2 * totalAgents) / 3) + 1; // Byzantine: 2f+1 where f is fault threshold
this.state = this.initializeState();
this.performance = {
consensusRounds: 0,
averageLatency: 0,
faultsDetected: 0,
successRate: 0,
};
}
private initializeState(): ConsensusState {
return {
currentView: 0,
sequenceNumber: 0,
phase: "pre-prepare",
leader: this.selectLeader(0),
activeAgents: new Set(),
proposals: new Map(),
messages: new Map(),
committed: new Set(),
};
}
/**
* Register an agent in the consensus network
*/
public registerAgent(agent: Agent): void {
this.agents.set(agent.id, agent);
this.state.activeAgents.add(agent.id);
this.emit("agent-registered", agent);
}
/**
* Remove an agent from the consensus network
*/
public removeAgent(agentId: string): void {
this.agents.delete(agentId);
this.state.activeAgents.delete(agentId);
this.emit("agent-removed", agentId);
}
/**
* Start consensus round for a proposal
*/
public async startConsensus(proposal: ConsensusProposal): Promise<boolean> {
const startTime = Date.now();
try {
if (!this.isLeader()) {
throw new Error("Only leader can start consensus");
}
this.state.sequenceNumber++;
this.state.proposals.set(proposal.id, proposal);
// Phase 1: Pre-prepare
await this.broadcastPrePrepare(proposal);
// Phase 2: Prepare
const prepareSuccess = await this.collectPrepareResponses(proposal.id);
if (!prepareSuccess) {
await this.initiateViewChange();
return false;
}
// Phase 3: Commit
const commitSuccess = await this.collectCommitResponses(proposal.id);
if (commitSuccess) {
this.state.committed.add(proposal.id);
this.updatePerformance(startTime, true);
this.emit("consensus-reached", proposal);
return true;
} else {
await this.initiateViewChange();
this.updatePerformance(startTime, false);
return false;
}
} catch (error) {
this.updatePerformance(startTime, false);
this.emit("consensus-error", error);
return false;
}
}
/**
* Process incoming consensus message
*/
public async processMessage(message: ConsensusMessage): Promise<void> {
if (!this.validateMessage(message)) {
this.emit("invalid-message", message);
return;
}
this.messageLog.push(message);
if (!this.state.messages.has(message.digest)) {
this.state.messages.set(message.digest, []);
}
this.state.messages.get(message.digest)!.push(message);
switch (message.type) {
case "pre-prepare":
await this.handlePrePrepare(message);
break;
case "prepare":
await this.handlePrepare(message);
break;
case "commit":
await this.handleCommit(message);
break;
case "view-change":
await this.handleViewChange(message);
break;
case "new-view":
await this.handleNewView(message);
break;
}
}
private async broadcastPrePrepare(
proposal: ConsensusProposal,
): Promise<void> {
const message: ConsensusMessage = {
type: "pre-prepare",
viewNumber: this.state.currentView,
sequenceNumber: this.state.sequenceNumber,
digest: proposal.hash,
payload: proposal,
timestamp: new Date(),
signature: this.signMessage(proposal.hash),
senderId: this.agentId,
};
this.state.phase = "pre-prepare";
await this.broadcastMessage(message);
}
private async handlePrePrepare(message: ConsensusMessage): Promise<void> {
if (message.senderId !== this.state.leader) {
this.emit("malicious-behavior", {
type: "unauthorized-pre-prepare",
agentId: message.senderId,
});
return;
}
if (message.viewNumber === this.state.currentView) {
// Send prepare message
const prepareMessage: ConsensusMessage = {
type: "prepare",
viewNumber: this.state.currentView,
sequenceNumber: message.sequenceNumber,
digest: message.digest,
payload: null,
timestamp: new Date(),
signature: this.signMessage(message.digest),
senderId: this.agentId,
};
await this.broadcastMessage(prepareMessage);
}
}
private async collectPrepareResponses(proposalId: string): Promise<boolean> {
return new Promise((resolve) => {
const requiredResponses = 2 * this.faultThreshold;
const receivedResponses = 0;
const timeout = setTimeout(() => {
resolve(false);
}, this.timeoutDuration);
const checkResponses = () => {
const proposal = this.state.proposals.get(proposalId);
if (!proposal) return;
const prepareMessages = this.state.messages.get(proposal.hash) || [];
const prepareCount = prepareMessages.filter(
(m) =>
m.type === "prepare" && m.viewNumber === this.state.currentView,
).length;
if (prepareCount >= requiredResponses) {
clearTimeout(timeout);
this.state.phase = "prepare";
resolve(true);
}
};
this.on("message-received", checkResponses);
// Initial check in case messages already arrived
checkResponses();
});
}
private async handlePrepare(message: ConsensusMessage): Promise<void> {
const requiredResponses = 2 * this.faultThreshold;
const proposal = Array.from(this.state.proposals.values()).find(
(p) => p.hash === message.digest,
);
if (!proposal) return;
const prepareMessages = this.state.messages.get(message.digest) || [];
const prepareCount = prepareMessages.filter(
(m) => m.type === "prepare" && m.viewNumber === this.state.currentView,
).length;
if (prepareCount >= requiredResponses) {
// Send commit message
const commitMessage: ConsensusMessage = {
type: "commit",
viewNumber: this.state.currentView,
sequenceNumber: message.sequenceNumber,
digest: message.digest,
payload: null,
timestamp: new Date(),
signature: this.signMessage(message.digest),
senderId: this.agentId,
};
await this.broadcastMessage(commitMessage);
}
}
private async collectCommitResponses(proposalId: string): Promise<boolean> {
return new Promise((resolve) => {
const requiredResponses = 2 * this.faultThreshold;
const timeout = setTimeout(() => {
resolve(false);
}, this.timeoutDuration);
const checkResponses = () => {
const proposal = this.state.proposals.get(proposalId);
if (!proposal) return;
const commitMessages = this.state.messages.get(proposal.hash) || [];
const commitCount = commitMessages.filter(
(m) => m.type === "commit" && m.viewNumber === this.state.currentView,
).length;
if (commitCount >= requiredResponses) {
clearTimeout(timeout);
this.state.phase = "commit";
resolve(true);
}
};
this.on("message-received", checkResponses);
checkResponses();
});
}
private async handleCommit(message: ConsensusMessage): Promise<void> {
this.emit("message-received", message);
}
private async initiateViewChange(): Promise<void> {
this.state.currentView++;
this.state.leader = this.selectLeader(this.state.currentView);
const viewChangeMessage: ConsensusMessage = {
type: "view-change",
viewNumber: this.state.currentView,
sequenceNumber: this.state.sequenceNumber,
digest: "",
payload: {
lastCommitted: Array.from(this.state.committed),
messageLog: this.messageLog.slice(-100), // Last 100 messages
},
timestamp: new Date(),
signature: this.signMessage(`view-change-${this.state.currentView}`),
senderId: this.agentId,
};
await this.broadcastMessage(viewChangeMessage);
this.emit("view-change-initiated", this.state.currentView);
}
private async handleViewChange(message: ConsensusMessage): Promise<void> {
// Collect view change messages and determine new leader
const viewChangeMessages = this.messageLog.filter(
(m) => m.type === "view-change" && m.viewNumber === message.viewNumber,
);
if (viewChangeMessages.length >= 2 * this.faultThreshold + 1) {
if (this.agentId === this.selectLeader(message.viewNumber)) {
await this.sendNewView(message.viewNumber);
}
}
}
private async sendNewView(viewNumber: number): Promise<void> {
const newViewMessage: ConsensusMessage = {
type: "new-view",
viewNumber,
sequenceNumber: this.state.sequenceNumber,
digest: "",
payload: {
viewChangeMessages: this.messageLog.filter(
(m) => m.type === "view-change" && m.viewNumber === viewNumber,
),
},
timestamp: new Date(),
signature: this.signMessage(`new-view-${viewNumber}`),
senderId: this.agentId,
};
await this.broadcastMessage(newViewMessage);
}
private async handleNewView(message: ConsensusMessage): Promise<void> {
if (message.senderId === this.selectLeader(message.viewNumber)) {
this.state.currentView = message.viewNumber;
this.state.leader = message.senderId;
this.state.phase = "pre-prepare";
this.emit("new-view-accepted", message.viewNumber);
}
}
private selectLeader(viewNumber: number): string {
const activeAgents = Array.from(this.state.activeAgents);
const leaderIndex = viewNumber % activeAgents.length;
return activeAgents[leaderIndex];
}
private isLeader(): boolean {
return this.agentId === this.state.leader;
}
private validateMessage(message: ConsensusMessage): boolean {
// Basic validation
if (!message.senderId || !message.signature || !message.timestamp) {
return false;
}
// Check if sender is registered
if (!this.agents.has(message.senderId)) {
return false;
}
// Validate signature (simplified - in real implementation, use proper crypto)
const expectedSignature = this.signMessage(message.digest || message.type);
// Additional Byzantine fault checks
const agent = this.agents.get(message.senderId)!;
if (agent.isMalicious) {
this.performance.faultsDetected++;
return false;
}
return true;
}
private signMessage(data: string): string {
// Simplified signing - in production, use proper cryptographic signatures
return createHash("sha256")
.update(data + this.agentId)
.digest("hex");
}
private async broadcastMessage(message: ConsensusMessage): Promise<void> {
// Simulate network broadcast
this.emit("broadcast-message", message);
// In a real implementation, this would send to all agents
setTimeout(() => {
this.emit("message-received", message);
}, Math.random() * 100); // Simulate network delay
}
private updatePerformance(startTime: number, success: boolean): void {
this.performance.consensusRounds++;
const latency = Date.now() - startTime;
this.performance.averageLatency =
(this.performance.averageLatency *
(this.performance.consensusRounds - 1) +
latency) /
this.performance.consensusRounds;
const successCount = success ? 1 : 0;
this.performance.successRate =
(this.performance.successRate * (this.performance.consensusRounds - 1) +
successCount) /
this.performance.consensusRounds;
}
/**
* Get current consensus state
*/
public getState(): ConsensusState {
return { ...this.state };
}
/**
* Get performance metrics
*/
public getPerformanceMetrics(): typeof this.performance {
return { ...this.performance };
}
/**
* Check if consensus can be reached with current network
*/
public canReachConsensus(): boolean {
const activeCount = this.state.activeAgents.size;
const maliciousCount = Array.from(this.agents.values()).filter(
(a) => a.isMalicious && this.state.activeAgents.has(a.id),
).length;
return (
maliciousCount <= this.faultThreshold && activeCount >= this.minQuorum
);
}
/**
* Get minimum quorum size for Byzantine consensus
*/
public getMinQuorum(): number {
return this.minQuorum;
}
/**
* Check if we have sufficient nodes for quorum
*/
public hasQuorum(): boolean {
return this.state.activeAgents.size >= this.minQuorum;
}
/**
* Simulate network partition
*/
public simulatePartition(agentIds: string[]): void {
agentIds.forEach((id) => this.state.activeAgents.delete(id));
this.emit("network-partition", agentIds);
}
/**
* Heal network partition
*/
public healPartition(agentIds: string[]): void {
agentIds.forEach((id) => {
if (this.agents.has(id)) {
this.state.activeAgents.add(id);
}
});
this.emit("network-healed", agentIds);
}
}
export default ByzantineConsensus;