package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
type Node struct {
id int
opinion int
stable int
byzantine bool
}
const (
b = 0.2
v = 5
l = 3
n = 5
q = 0.2
)
func helpWeakest(nodes []Node) int {
zeros, ones := 0, 0
for _, nd := range nodes {
if !nd.byzantine {
if nd.opinion == 0 {
zeros++
} else {
ones++
}
}
}
if zeros <= ones {
return 0
}
return 1
}
func main() {
rand.Seed(time.Now().UnixNano())
// Timeout global
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
nodes := make([]Node, n)
// Determine Byzantine count
nByzantine := int(float64(n) * q)
if nByzantine > n/2 {
nByzantine = n / 2
}
// Init random opinions
for i := 0; i < n; i++ {
nodes[i] = Node{id: i + 1, opinion: rand.Intn(2)}
}
// Random Byzantine assignment
perm := rand.Perm(n)
for i := 0; i < nByzantine; i++ {
nodes[perm[i]].byzantine = true
}
start := time.Now()
for {
select {
case <-ctx.Done():
return "TimeOut sur l'execution, vous devriez modifier les paramètres d'entrée"
default:
// Continue execution
}
allStable := true
newOpinions := make([]int, n)
var wg sync.WaitGroup
for i := range nodes {
wg.Add(1)
go func(i int) {
defer wg.Done()
select {
case <-ctx.Done():
return
default:
}
node := &nodes[i]
if node.byzantine {
newOpinions[i] = helpWeakest(nodes)
return
}
sum := 0
for j := 0; j < l; j++ {
target := rand.Intn(n)
sum += nodes[target].opinion
}
eta := float64(sum) / float64(l)
U := b + rand.Float64()*(1-2*b)
if eta > U {
newOpinions[i] = 1
} else if eta < U {
newOpinions[i] = 0
} else {
newOpinions[i] = node.opinion
}
}(i)
}
wg.Wait()
// Apply updates
for i := range nodes {
if nodes[i].opinion == newOpinions[i] {
nodes[i].stable++
} else {
nodes[i].stable = 0
}
nodes[i].opinion = newOpinions[i]
if nodes[i].stable < v {
allStable = false
}
}
if allStable {
break
}
}
elapsed := time.Since(start)
fmt.Println("Final opinions:")
for _, nd := range nodes {
fmt.Printf("Node %d (Byz=%v): %d\n", nd.id, nd.byzantine, nd.opinion)
}
fmt.Printf("Time elapsed: %v\n", elapsed)
// Check consensus of honest nodes
ref := -1
consensus := true
for _, nd := range nodes {
if nd.byzantine {
continue
}
if ref == -1 {
ref = nd.opinion
} else if nd.opinion != ref {
consensus = false
}
}
if consensus {
fmt.Println("Honest nodes reached consensus.")
} else {
fmt.Println("Honest nodes did NOT reach consensus.")
}
}q:
0.2n:
5:
3v:
5:
0.2package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
type Node struct {
id int
opinion int
stable int
byzantine bool
}
type RoundSnapshot struct {
Opinions []int // opinions de tous les noeuds à la fin du round
ByzMask []bool // quels noeuds sont déjà marqués comme byzantins avant ce round
Time time.Time
}
const (
b = 0.2
v = 5
l = 3
n = 5
q = 0.2
)
func helpWeakest(nodes []Node) int {
zeros, ones := 0, 0
for _, nd := range nodes {
if !nd.byzantine {
if nd.opinion == 0 {
zeros++
} else {
ones++
}
}
}
if zeros <= ones {
return 0
}
return 1
}
func detectByzantineNodes(history []RoundSnapshot, nodes []Node,
minRounds int, wFlip, wDisagree, detectThreshold float64) []int {
rounds := len(history)
n := len(nodes)
if rounds < minRounds {
return nil // pas assez de données
}
majorities := make([]int, rounds)
for r := 0; r < rounds; r++ {
cnt1, cnt0 := 0, 0
for i := 0; i < n; i++ {
if nodes[i].byzantine {
continue
}
if history[r].Opinions[i] == 1 {
cnt1++
} else {
cnt0++
}
}
if cnt1 >= cnt0 {
majorities[r] = 1
} else {
majorities[r] = 0
}
}
var suspects []int
for i := 0; i < n; i++ {
if nodes[i].byzantine {
continue
}
flips := 0
for r := 1; r < rounds; r++ {
if history[r].Opinions[i] != history[r-1].Opinions[i] {
flips++
}
}
flipRate := float64(flips) / float64(rounds-1)
disagrees := 0
for r := 0; r < rounds; r++ {
if history[r].Opinions[i] != majorities[r] {
disagrees++
}
}
disagreeRate := float64(disagrees) / float64(rounds)
score := wFlip*flipRate + wDisagree*disagreeRate
if score > detectThreshold {
suspects = append(suspects, i)
}
}
return suspects
}
func main() {
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
nodes := make([]Node, n)
history := []RoundSnapshot{}
// placement byzantins initiaux
nByzantine := int(float64(n) * q)
if nByzantine > n/2 {
nByzantine = n / 2
}
perm := rand.Perm(n)
for i := 0; i < n; i++ {
nodes[i] = Node{id: i + 1, opinion: rand.Intn(2)}
}
for i := 0; i < nByzantine; i++ {
nodes[perm[i]].byzantine = true
}
start := time.Now()
for {
select {
case <-ctx.Done():
return "TimeOut sur l'execution, vous devriez modifier les paramètres d'entrée"
default:
}
allStable := true
newOpinions := make([]int, n)
var wg sync.WaitGroup
// --- Computation parallel ---
for i := range nodes {
wg.Add(1)
go func(i int) {
defer wg.Done()
select {
case <-ctx.Done():
return
default:
}
nd := &nodes[i]
if nd.byzantine {
newOpinions[i] = helpWeakest(nodes)
return
}
sum := 0
for k := 0; k < l; k++ {
target := rand.Intn(n)
sum += nodes[target].opinion
}
eta := float64(sum) / float64(l)
U := b + rand.Float64()*(1-2*b)
if eta > U {
newOpinions[i] = 1
} else if eta < U {
newOpinions[i] = 0
} else {
newOpinions[i] = nd.opinion
}
}(i)
}
wg.Wait()
// --- Mise à jour des opinions ---
for i := range nodes {
if nodes[i].opinion == newOpinions[i] {
nodes[i].stable++
} else {
nodes[i].stable = 0
}
nodes[i].opinion = newOpinions[i]
if nodes[i].stable < v {
allStable = false
}
}
// --- Enregistrement du snapshot ---
snap := RoundSnapshot{
Opinions: make([]int, n),
ByzMask: make([]bool, n),
Time: time.Now(),
}
// on copie les états **avant** les mises à jour de ce round
for i := 0; i < n; i++ {
snap.Opinions[i] = nodes[i].opinion
snap.ByzMask[i] = nodes[i].byzantine
}
history = append(history, snap)
// --- Détection byzantine ---
suspects := detectByzantineNodes(history, nodes, 6, 0.6, 0.6, 0.7)
for _, idx := range suspects {
if !nodes[idx].byzantine {
nodes[idx].byzantine = true
fmt.Printf("DETECTION: Node %d marked as Byzantine\n", nodes[idx].id)
}
}
if allStable {
break
}
}
elapsed := time.Since(start)
fmt.Println("\nFinal opinions:")
for _, nd := range nodes {
fmt.Printf("Node %d (Byz=%v): %d\n", nd.id, nd.byzantine, nd.opinion)
}
fmt.Printf("Time elapsed: %v\n", elapsed)
// consensus final
ref := -1
consensus := true
for _, nd := range nodes {
if nd.byzantine {
continue
}
if ref == -1 {
ref = nd.opinion
} else if nd.opinion != ref {
consensus = false
}
}
if consensus {
fmt.Println("Honest nodes reached consensus.")
} else {
fmt.Println("Honest nodes did NOT reach consensus.")
}
}q:
0.2n:
5:
3v:
5:
0.2