Skip to content

Commit

Permalink
Merge pull request #7 from HannahMarsh/5-have-bulletin-board-distribu…
Browse files Browse the repository at this point in the history
…te-configurations

5 have bulletin board distribute configurations
  • Loading branch information
DrIsmailBadrez authored Oct 4, 2024
2 parents 54a2e3e + 1e6ca45 commit a91a43b
Show file tree
Hide file tree
Showing 29 changed files with 1,036 additions and 914 deletions.
47 changes: 15 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ view when Alice sends a message to Carol instead.
### Parameters
(_also defined in [/config/config.yml](config/config.yml)_)

- **$N$**: The minimum number of clients.
- **$n$**: The minimum number of relays.
- **$x$**: Server load (i.e. the expected number of onions each relay processes per round).
- **$\ell_1$**: The number of mixers in each routing path.
- **$\ell_2$**: The number of gatekeepers in each routing path.
- **$L$**: The number of rounds (also the length of the routing path, equal to $`\ell_1 + \ell_2 + 1`$ ).
- **$R$**: The number of participating relays.
- **$N$**: The number of participating clients.
- **$d$**: The number of non-null key-blocks in $S_1$. (thus $d$ is the threshold for number of bruises before an onion is discard by a gatekeeper).
- **$\tau$**: ( $\tau \lt \(1 − \gamma\)\(1 − \chi\)$ ) The fraction of expected checkpoint onions needed for a relay to progress its local clock.
- **$\epsilon$**: The privacy loss in the worst case scenario.
Expand All @@ -87,6 +87,8 @@ view when Alice sends a message to Carol instead.
- **$\theta$**: The maximum fraction of bruisable layers that can be bruised before the innermost tulip bulb becomes
unrecoverable. Note that $d = \theta \cdot \ell_1$
- **$\chi$**: The fraction of $N$ relays that can be corrupted and controlled by the adversary (the subset is chosen prior to execution). Note that $\chi \lt \theta - 0.5$ and $\chi \lt \frac{d}{\ell_1} - 0.5$
- **`BulletinBoardUrl`**: The IP address and port of the bulletin board.
- **`MetricsPort`**: The port that all aggregated metrics are served (on the Bulletin board's IP address).

### No Global Clock:

Expand Down Expand Up @@ -522,7 +524,7 @@ go test -v ./...
Usage
-----

All configurations are set in the [`config/config.yaml`](config/config/yaml) file.
All configurations are initialized in the [`config/config.yaml`](config/config/yaml) file.

### Running the Bulletin Board

Expand All @@ -535,60 +537,41 @@ go run cmd/bulletin-board/main.go -logLevel=<logLevel>
### Running a Relay

```bash
go run cmd/relay/main.go -id=<id> -logLevel=<logLevel>
go run cmd/relay/main.go -id=<id> -host=<host> -port=<port> -promPort=<promPort> -logLevel=<logLevel>
```
- Options:
- `<id>`: The unique identifier for the relay.
- `<host>`: (optional) The public host IP for the relay. If not given, the public IP will be retrieved automatically.
- `<port>`: The port number for the relay.
- `<promPort>`: The port number for scraping the relay's Prometheus metrics.
- `<logLevel>`: (optional) The logging level (e.g., "info", "debug", "warn", "error").

### Running a Client

```bash
go run cmd/client/main.go -id=<id> -logLevel=<logLevel>
go run cmd/client/main.go -id=<id> -host=<host> -port=<port> -promPort=<promPort> -logLevel=<logLevel>
```
- Options:
- `<id>`: The unique identifier for the client.
- `<host>`: (optional) The public host IP for the client. If not given, the public IP will be retrieved automatically.
- `<port>`: The port number for the client.
- `<promPort>`: The port number for scraping the client's Prometheus metrics.
- `<logLevel>`: (optional) The logging level (e.g., "info", "debug", "warn", "error").

### Running the Metric Collector (Scrapes Prometheus endpoints for Clients and relays)
```bash
go run cmd/metrics/main.go -logLevel=<slogLevel>
```
- Options:
- `<logLevel>`: (optional) The logging level (e.g., "info", "debug", "warn", "error").

### Running the Browser-Based Visualization Server

```bash
go run cmd/visualizer/visualizer.go -port <port>
```
- Options:
- `<port>`: The port number for the visualization server. Access at `http://localhost:<port>`.

## Endpoints

### Bulletin Board

- **Register Client**: `POST /registerClient`
- **Register Relay**: `POST /registerRelay`
- **Updating config with new parameters (for next run)**: `POST /updateConfig`

### Relay & Client

- **Receive Onion**: `POST /receive`
- **Get Status**: `GET /status`
- **Start Run**: `POST /start`
- **Prometheus Metrics**: `GET /metrics` - Note that this is served on a different port which is specified in the [config.yml](/config/config.yml) file

When implementing the onion routing protocol, it helps to run the visualization server
in real time to view the messages and onions processes by each client and relay. For a small number of clients/relays, this makes
debugging the protocol easier.

Obviously, it is not recommended to run the visualization program once we deploy the simulation in a distributed environment (with potentially hundreds of relays and rounds).

![](img/vis.png)

![](img/demo.gif)

- **Prometheus Metrics**: `GET /metrics` - Note that this is served on a different port

---

Expand Down
20 changes: 8 additions & 12 deletions cmd/bulletin-board/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,15 @@ func main() {
}

// Initialize global configurations by loading them from config/config.yml
if err, path := config.InitGlobal(); err != nil {
if err, _ := config.InitGlobal(); err != nil {
slog.Error("failed to init config", err)
os.Exit(1)
} else if err = config.InitPrometheusConfig(path); err != nil {
slog.Error("failed to init prometheus config", err)
os.Exit(1)
}

cfg := config.GlobalConfig

host := cfg.BulletinBoard.Host
port := cfg.BulletinBoard.Port
// Construct the full URL for the Bulletin Board
url := fmt.Sprintf("https://%s:%d", host, port)
url := fmt.Sprintf("https://%s:%d", config.GetBulletinBoardHost(), config.GetBulletinBoardPort())

slog.Info("⚡ init Bulletin board")
slog.Info("⚡ init Bulletin board", "url", url)

// Create a new instance of the Bulletin Board with the current configuration.
bulletinBoard := bulletin_board.NewBulletinBoard()
Expand All @@ -76,8 +69,9 @@ func main() {
// Set up HTTP handlers
http.HandleFunc("/registerRelay", bulletinBoard.HandleRegisterRelay)
http.HandleFunc("/registerClient", bulletinBoard.HandleRegisterClient)
http.HandleFunc("/registerIntentToSend", bulletinBoard.HandleRegisterIntentToSend)
//http.HandleFunc("/registerIntentToSend", bulletinBoard.HandleRegisterIntentToSend)
http.HandleFunc("/updateRelay", bulletinBoard.HandleUpdateRelayInfo)
http.HandleFunc("/updateConfig", bulletinBoard.HandleUpdateConfig)
http.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) {
slog.Info("Shutdown signal received")
quit <- os.Signal(syscall.SIGTERM) // signal shutdown
Expand All @@ -89,7 +83,7 @@ func main() {

// Start the HTTP server
go func() {
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
if err := http.ListenAndServe(fmt.Sprintf(":%d", config.GetBulletinBoardPort()), nil); err != nil {
if errors.Is(err, http.ErrServerClosed) { // Check if the server was closed intentionally (normal shutdown).
slog.Info("HTTP server closed")
} else {
Expand All @@ -104,8 +98,10 @@ func main() {
select {
case v := <-quit: // OS signal is received
config.GlobalCancel()
bulletinBoard.Shutdown()
slog.Info("", "signal.Notify", v)
case done := <-config.GlobalCtx.Done(): // global context is canceled
slog.Info("", "ctx.Done", done)
bulletinBoard.Shutdown()
}
}
61 changes: 29 additions & 32 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/HannahMarsh/pi_t-experiment/config"
"github.com/HannahMarsh/pi_t-experiment/internal/metrics"
"github.com/HannahMarsh/pi_t-experiment/internal/model/client"
"github.com/HannahMarsh/pi_t-experiment/pkg/utils"
"go.uber.org/automaxprocs/maxprocs"
"log/slog"
"net/http"
Expand All @@ -21,8 +22,11 @@ import (

func main() {
// Define command-line flags
id := flag.Int("id", -1, "ID of the newClient (required)")
logLevel := flag.String("log-level", "debug", "Log level")
id_ := flag.Int("id", -1, "ID of the newClient (required)")
ip_ := flag.String("host", "x", "IP address of the client")
port_ := flag.Int("port", 8080, "Port of the client")
promPort_ := flag.Int("promPort", 8200, "Port of the client's Prometheus metrics")
logLevel_ := flag.String("log-level", "debug", "Log level")

flag.Usage = func() {
if _, err := fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0]); err != nil {
Expand All @@ -33,15 +37,30 @@ func main() {

flag.Parse()

id := *id_
ip := *ip_
port := *port_
promPort := *promPort_
logLevel := *logLevel_

pl.SetUpLogrusAndSlog(logLevel)

// Check if the required flag is provided
if *id == -1 {
if id == -1 {
_, _ = fmt.Fprintf(os.Stderr, "Error: the -id flag is required\n")
flag.Usage()
os.Exit(2)
}

// Set up logrus with the specified log level.
pl.SetUpLogrusAndSlog(*logLevel)
if ip == "x" {
IP, err := utils.GetPublicIP()
if err != nil {
slog.Error("failed to get public IP", err)
os.Exit(1)
}
slog.Info("", "IP", IP.IP, "Hostname", IP.HostName, "City", IP.City, "Region", IP.Region, "Country", IP.Country, "Location", IP.Location, "Org", IP.Org, "Postal", IP.Postal, "Timezone", IP.Timezone, "ReadMe", IP.ReadMe)
ip = IP.IP
}

// Automatically adjust the GOMAXPROCS setting based on the number of available CPU cores.
if _, err := maxprocs.Set(); err != nil {
Expand All @@ -55,31 +74,12 @@ func main() {
os.Exit(1)
}

cfg := config.GlobalConfig

// Find the client configuration that matches the provided ID.
var clientConfig *config.Client
for _, c := range cfg.Clients {
if c.ID == *id {
clientConfig = &c
break
}
}

if clientConfig == nil {
pl.LogNewError("Invalid id. Failed to get newClient config for id=%d", *id)
os.Exit(1)
}

slog.Info("⚡ init newClient", "id", *id)

// Construct the full URL for the Bulletin Board
bulletinBoardAddress := fmt.Sprintf("http://%s:%d", cfg.BulletinBoard.Host, cfg.BulletinBoard.Port)
slog.Info("⚡ init newClient", "id", id)

var newClient *client.Client
// Attempt to create a new client instance, retrying every 5 seconds upon failure (in case the bulletin board isn't ready yet).
for {
if n, err := client.NewClient(clientConfig.ID, clientConfig.Host, clientConfig.Port, bulletinBoardAddress); err != nil {
if n, err := client.NewClient(id, ip, port, promPort, config.GetBulletinBoardAddress()); err != nil {
slog.Error("failed to create new c. Trying again in 5 seconds. ", err)
time.Sleep(5 * time.Second)
continue
Expand Down Expand Up @@ -109,22 +109,19 @@ func main() {
})

// Serve Prometheus metrics in a separate goroutine.
shutdownMetrics := metrics.ServeMetrics(clientConfig.PrometheusPort, metrics.MSG_SENT, metrics.MSG_RECEIVED, metrics.ONION_SIZE)
shutdownMetrics := metrics.ServeMetrics(promPort, metrics.MSG_SENT, metrics.MSG_RECEIVED, metrics.ONION_SIZE)

// Start the HTTP server
go func() {
// Construct the address for the HTTP server based on the client's port.
address := fmt.Sprintf(":%d", clientConfig.Port)
address := fmt.Sprintf(":%d", port)
// Attempt to start the HTTP server.
if err2 := http.ListenAndServe(address, nil); err2 != nil && !errors.Is(err2, http.ErrServerClosed) {
slog.Error("failed to start HTTP server", err2)
}
}()

slog.Info("🌏 start newClient...", "address", fmt.Sprintf("http://%s:%d", clientConfig.Host, clientConfig.Port))

// Start generating messages in a separate goroutine.
go newClient.StartGeneratingMessages()
slog.Info("🌏 start newClient...", "address", fmt.Sprintf("http://%s:%d", ip, port))

// Wait for either an OS signal to quit or the global context to be canceled
select {
Expand Down
122 changes: 0 additions & 122 deletions cmd/metrics/main.go

This file was deleted.

Loading

0 comments on commit a91a43b

Please sign in to comment.