Skip to content

Commit

Permalink
Add bcb-demo
Browse files Browse the repository at this point in the history
In the demo, the first node sends a single message to all other
nodes using Byzantine Consistent Broadcast.
  • Loading branch information
xosmig committed Aug 4, 2022
1 parent d043606 commit 0c4415b
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 0 deletions.
131 changes: 131 additions & 0 deletions samples/bcb-demo/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package main

import (
"context"
"fmt"
"os"

"gopkg.in/alecthomas/kingpin.v2"

"github.com/filecoin-project/mir"
"github.com/filecoin-project/mir/pkg/bcb"
mirCrypto "github.com/filecoin-project/mir/pkg/crypto"
"github.com/filecoin-project/mir/pkg/logging"
"github.com/filecoin-project/mir/pkg/modules"
"github.com/filecoin-project/mir/pkg/net/grpc"
t "github.com/filecoin-project/mir/pkg/types"
grpctools "github.com/filecoin-project/mir/pkg/util/grpc"
)

const (

// Base port number for the nodes to listen to messages from each other.
// The nodes will listen on ports starting from nodeBasePort through nodeBasePort+3.
// Note that protocol messages and requests are following two completely distinct paths to avoid interference
// of clients with node-to-node communication.
nodeBasePort = 10000

// The number of nodes participating in the chat.
nodeNumber = 4

// The index of the leader node of BCB.
leaderNode = 0
)

func main() {
if err := run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func run() error {
args := parseArgs(os.Args)

// IDs of nodes that are part of the system.
// This example uses a static configuration of nodeNumber nodes.
nodeIDs := make([]t.NodeID, nodeNumber)
for i := 0; i < nodeNumber; i++ {
nodeIDs[i] = t.NewNodeIDFromInt(i)
}

nodeAddrs := make(map[t.NodeID]t.NodeAddress)
for i := range nodeIDs {
nodeAddrs[t.NewNodeIDFromInt(i)] = t.NodeAddress(grpctools.NewDummyMultiaddr(i + nodeBasePort))
}

transportModule, err := grpc.NewTransport(args.OwnID, nodeAddrs[args.OwnID], logging.NilLogger)
if err != nil {
return fmt.Errorf("failed to get network transport %w", err)
}
if err := transportModule.Start(); err != nil {
return fmt.Errorf("could not start network transport: %w", err)
}
transportModule.Connect(context.Background(), nodeAddrs)

bcbModule := bcb.NewModule(
&bcb.ModuleConfig{
Self: "bcb",
Consumer: "control",
Net: "net",
Crypto: "crypto",
},
&bcb.ModuleParams{
InstanceUID: []byte("testing instance"),
AllNodes: nodeIDs,
Leader: nodeIDs[leaderNode],
},
args.OwnID,
)

// control module reads the user input from the console and processes it.
control := newControlModule( /*isLeader=*/ args.OwnID == nodeIDs[leaderNode])

m := map[t.ModuleID]modules.Module{
"net": transportModule,
"crypto": mirCrypto.New(&mirCrypto.DummyCrypto{DummySig: []byte{0}}),
"bcb": bcbModule,
"control": control,
}

// create a Mir node
node, err := mir.NewNode("client", &mir.NodeConfig{Logger: logging.NilLogger}, m, nil, nil)
if err != nil {
return fmt.Errorf("error creating a Mir node: %w", err)
}

// run the node
err = node.Run(context.Background())
if err != nil {
return fmt.Errorf("error running node: %w", err)
}

return nil
}

// Parses the command-line arguments and returns them in a params struct.
func parseArgs(args []string) *parsedArgs {
app := kingpin.New("chat-demo", "Small chat application to demonstrate the usage of the Mir library.")
verbose := app.Flag("verbose", "Verbose mode.").Short('v').Bool()
ownID := app.Arg("id", "ID of this node").Required().String()

if _, err := app.Parse(args[1:]); err != nil { // Skip args[0], which is the name of the program, not an argument.
app.FatalUsage("could not parse arguments: %v\n", err)
}

return &parsedArgs{
OwnID: t.NodeID(*ownID),
Verbose: *verbose,
}
}

// parsedArgs represents parsed command-line parameters passed to the program.
type parsedArgs struct {

// ID of this node.
// The package github.com/hyperledger-labs/mir/pkg/types defines this and other types used by the library.
OwnID t.NodeID

// If set, print verbose output to stdout.
Verbose bool
}
94 changes: 94 additions & 0 deletions samples/bcb-demo/control-module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"bufio"
"context"
"fmt"
"os"

"github.com/filecoin-project/mir/pkg/events"
"github.com/filecoin-project/mir/pkg/modules"
"github.com/filecoin-project/mir/pkg/pb/bcbpb"
"github.com/filecoin-project/mir/pkg/pb/eventpb"
)

type controlModule struct {
eventsOut chan *events.EventList
isLeader bool
}

func newControlModule(isLeader bool) modules.ActiveModule {
return &controlModule{
eventsOut: make(chan *events.EventList),
isLeader: isLeader,
}
}

func (m *controlModule) ImplementsModule() {}

func (m *controlModule) ApplyEvents(ctx context.Context, events *events.EventList) error {
iter := events.Iterator()
for event := iter.Next(); event != nil; event = iter.Next() {
switch event.Type.(type) {

case *eventpb.Event_Init:
if m.isLeader {
go func() {
err := m.readMessageFromConsole()
if err != nil {
panic(err)
}
}()
} else {
fmt.Println("Waiting for the message...")
}

case *eventpb.Event_Bcb:
bcbEvent := event.Type.(*eventpb.Event_Bcb).Bcb
switch bcbEvent.Type.(type) {

case *bcbpb.Event_Deliver:
deliverEvent := bcbEvent.Type.(*bcbpb.Event_Deliver).Deliver
fmt.Println("Leader says: ", string(deliverEvent.Data))

default:
return fmt.Errorf("unknown bcb event type: %T", bcbEvent.Type)
}

default:
return fmt.Errorf("unknown event type: %T", event.Type)
}
}

return nil
}

func (m *controlModule) EventsOut() <-chan *events.EventList {
return m.eventsOut
}

func (m *controlModule) readMessageFromConsole() error {
// Read the user input
scanner := bufio.NewScanner(os.Stdin)

fmt.Print("Type in a message and press Enter: ")
scanner.Scan()
if scanner.Err() != nil {
return fmt.Errorf("error reading from console: %w", scanner.Err())
}

m.eventsOut <- events.ListOf(&eventpb.Event{
DestModule: "bcb",
Type: &eventpb.Event_Bcb{
Bcb: &bcbpb.Event{
Type: &bcbpb.Event_Request{
Request: &bcbpb.Request{
Data: []byte(scanner.Text()),
},
},
},
},
})

return nil
}
19 changes: 19 additions & 0 deletions samples/bcb-demo/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
NODE_0_LOG="./node_0.log"
NODE_1_LOG="./node_1.log"
NODE_2_LOG="./node_2.log"
NODE_3_LOG="./node_3.log"

rm -rf ./node_*.log

tmux new-session -d -s "demo" \; \
new-window -t "demo" \; \
\
split-window -t "demo:0" -v \; \
split-window -t "demo:0.0" -h \; \
split-window -t "demo:0.2" -h \; \
\
send-keys -t "demo:0.0" "go run ./samples/bcb-demo 0 2>&1 | tee $NODE_0_LOG" Enter \; \
send-keys -t "demo:0.1" "go run ./samples/bcb-demo 1 2>&1 | tee $NODE_1_LOG" Enter \; \
send-keys -t "demo:0.2" "go run ./samples/bcb-demo 2 2>&1 | tee $NODE_2_LOG" Enter \; \
send-keys -t "demo:0.3" "go run ./samples/bcb-demo 3 2>&1 | tee $NODE_3_LOG" Enter \; \
attach-session -t "demo:0.0"

0 comments on commit 0c4415b

Please sign in to comment.