-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
In the demo, the first node sends a single message to all other nodes using Byzantine Consistent Broadcast.
- Loading branch information
Showing
3 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |