Skip to content

Commit

Permalink
Update client and node configurations to accommodate new features and…
Browse files Browse the repository at this point in the history
… fixes.

This commit refactors the client and node configurations to remove hardcoded values and adapt to changes in the onion routing system. Specifically, it updates the `num_rounds` value in the `config/config.yml` file to 5. Additionally, the `AddOnion` and `AddReceived` functions in the `clientStatus.go` file were updated to include new fields and handle checkpoint onions correctly. The `nodes` and `checkpointOnions` functionality was also fixed in the `nodeStatus.go` file. The `Client` struct in `client.go` was updated to handle dummy messages and calculate the checksum of the onion. Furthermore, the `Receive` function in the `node.go` file was fixed to correctly handle peeled onions. Finally, the `NewNode` function in the `node.go` file was updated to include the expected checkpoints and handle the creation of new nodes.
  • Loading branch information
HannahMarsh committed Jun 25, 2024
1 parent 92ad93f commit d45fa9b
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 143 deletions.
2 changes: 1 addition & 1 deletion config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ server_load: 7
heartbeat_interval: 5
min_nodes: 2
min_total_messages: 1
num_rounds: 4
num_rounds: 5
max_bruises: 2
epsilon: 0.1
delta: 1e-5
Expand Down
2 changes: 1 addition & 1 deletion internal/api/clientStatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (cs *ClientStatus) AddSent(clientReceiver PublicNodeApi, routingPath []Publ
TimeSent: time.Now(),
})

slog.Info(PrettyLogger.GetFuncName(), "message", message)
// slog.Info(PrettyLogger.GetFuncName(), "message", message)
}

func (cs *ClientStatus) AddReceived(message Message) {
Expand Down
7 changes: 1 addition & 6 deletions internal/api/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package api

import (
"github.com/HannahMarsh/pi_t-experiment/pkg/utils"
"golang.org/x/exp/slog"
)

type Message struct {
Expand All @@ -13,11 +12,7 @@ type Message struct {
}

func NewMessage(from, to, msg string) Message {
h, err := utils.GenerateUniqueHash()
if err != nil {
slog.Error("failed to generate unique hash", err)
h = ""
}
h := utils.GenerateUniqueHash()
return Message{
From: from,
To: to,
Expand Down
41 changes: 37 additions & 4 deletions internal/api/nodeStatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
)

type NodeStatus struct {
Received []OnionStatus
Node PublicNodeApi
mu sync.RWMutex
Received []OnionStatus
Node PublicNodeApi
CheckpointOnionsReceived map[int]int
ExpectedCheckpoints map[int]int
mu sync.RWMutex
}

type OnionStatus struct {
Expand All @@ -22,9 +24,38 @@ type OnionStatus struct {
TimeReceived time.Time
Bruises int
Dropped bool
NonceVerification bool
ExpectCheckPoint bool
}

func (ns *NodeStatus) AddOnion(lastHop, thisAddress, nextHop string, layer int, isCheckPointOnion bool, bruises int, dropped bool) {
func NewNodeStatus(id int, address, publicKey string, isMixer bool) *NodeStatus {
return &NodeStatus{
Received: make([]OnionStatus, 0),
Node: PublicNodeApi{
ID: id,
Address: address,
PublicKey: publicKey,
Time: time.Now(),
IsMixer: isMixer,
},
CheckpointOnionsReceived: make(map[int]int),
ExpectedCheckpoints: make(map[int]int),
}
}

func (ns *NodeStatus) AddCheckpointOnion(layer int) {
ns.mu.Lock()
defer ns.mu.Unlock()
ns.CheckpointOnionsReceived[layer]++
}

func (ns *NodeStatus) AddExpectedCheckpoint(layer int) {
ns.mu.Lock()
defer ns.mu.Unlock()
ns.ExpectedCheckpoints[layer]++
}

func (ns *NodeStatus) AddOnion(lastHop, thisAddress, nextHop string, layer int, isCheckPointOnion bool, bruises int, dropped bool, nonceVerification bool, expectCheckPoint bool) {
ns.mu.Lock()
defer ns.mu.Unlock()
ns.Received = append(ns.Received, OnionStatus{
Expand All @@ -36,6 +67,8 @@ func (ns *NodeStatus) AddOnion(lastHop, thisAddress, nextHop string, layer int,
TimeReceived: time.Now(),
Bruises: bruises,
Dropped: dropped,
NonceVerification: nonceVerification,
ExpectCheckPoint: expectCheckPoint,
})
}

Expand Down
99 changes: 76 additions & 23 deletions internal/model/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ func DetermineRoutingPath(pathLength int, participants []api.PublicNodeApi) ([]a
return selectedNodes, nil
}

func DetermineCheckpointRoutingPath(pathLength int, nodes []api.PublicNodeApi, participatingClients []api.PublicNodeApi, checkpointReceiver api.PublicNodeApi, round int) ([]api.PublicNodeApi, error) {
path, err := DetermineRoutingPath(pathLength-1, utils.Remove(nodes, func(p api.PublicNodeApi) bool {
return p.Address == checkpointReceiver.Address
}))
if err != nil {
return nil, pl.WrapError(err, "failed to determine routing path")
}
return append(utils.InsertAtIndex(path, round, checkpointReceiver), utils.RandomElement(participatingClients)), nil
}

func (c *Client) formOnions(start api.StartRunApi) (map[string][]api.OnionApi, error) {

onions := make(map[string][]api.OnionApi)
Expand All @@ -187,26 +197,26 @@ func (c *Client) formOnions(start api.StartRunApi) (map[string][]api.OnionApi, e
return node.Address != c.Adddress && node.Address != ""
})

numMessagesToSend := make(map[string]int)

for _, msg := range c.Messages {
if _, found := numMessagesToSend[msg.To]; !found {
numMessagesToSend[msg.To] = 0
}
numMessagesToSend[msg.To]++
}

dummyNum := 0

for addr, numMessages := range numMessagesToSend {
if numMessages < start.NumMessagesPerClient {
numDummyNeeded := start.NumMessagesPerClient - numMessages
for i := 0; i < numDummyNeeded; i++ {
c.Messages = append(c.Messages, api.NewMessage(c.Adddress, addr, fmt.Sprintf("dummy%d", dummyNum)))
dummyNum++
}
}
}
//numMessagesToSend := make(map[string]int)
//
//for _, msg := range c.Messages {
// if _, found := numMessagesToSend[msg.To]; !found {
// numMessagesToSend[msg.To] = 0
// }
// numMessagesToSend[msg.To]++
//}
//
//dummyNum := 0
//
//for addr, numMessages := range numMessagesToSend {
// if numMessages < start.NumMessagesPerClient {
// numDummyNeeded := start.NumMessagesPerClient - numMessages
// for i := 0; i < numDummyNeeded; i++ {
// c.Messages = append(c.Messages, api.NewMessage(c.Adddress, addr, fmt.Sprintf("dummy%d", dummyNum)))
// dummyNum++
// }
// }
//}

for _, msg := range c.Messages {
if destination, found := utils.Find(start.ParticipatingClients, api.PublicNodeApi{}, func(client api.PublicNodeApi) bool {
Expand All @@ -228,13 +238,56 @@ func (c *Client) formOnions(start api.StartRunApi) (map[string][]api.OnionApi, e
return node.Address
})
slog.Info("routing path", "path", addresses)
if addr, onion, err3 := pi_t.FormOnion(c.PrivateKey, c.PublicKey, msgString, publicKeys, addresses, -1); err3 != nil {
if addr, onion, checkpoints, err3 := pi_t.FormOnion(c.PrivateKey, c.PublicKey, msgString, publicKeys, addresses, -1); err3 != nil {
return nil, pl.WrapError(err3, "failed to create onion")
} else {
// generate checkpoint onions
for i, node := range routingPath {
if checkpoints[i] {
path, err5 := DetermineCheckpointRoutingPath(config.GlobalConfig.Rounds, nodes, utils.Filter(start.ParticipatingClients, func(publicNodeApi api.PublicNodeApi) bool {
return publicNodeApi.Address != c.Adddress && publicNodeApi.Address != ""
}), node, i)
if err5 != nil {
return nil, pl.WrapError(err5, "failed to determine checkpoint routing path")
}

checkPointPublicKeys := utils.Map(path, func(node api.PublicNodeApi) string {
return node.PublicKey
})
checkPointAddresses := utils.Map(path, func(node api.PublicNodeApi) string {
return node.Address
})

dummyMsg := api.Message{
From: c.Adddress,
To: utils.GetLast(path).Address,
Msg: "checkpoint onion",
Hash: utils.GenerateUniqueHash(),
}
dummyPayload, err6 := json.Marshal(dummyMsg)
if err6 != nil {
return nil, pl.WrapError(err6, "failed to marshal dummy message")
}
if firstHop, checkpointOnion, _, err4 := pi_t.FormOnion(c.PrivateKey, c.PublicKey, dummyPayload, checkPointPublicKeys, checkPointAddresses, -i); err4 != nil {
return nil, pl.WrapError(err4, "failed to create onion")
} else {
if _, present := onions[firstHop]; !present {
onions[firstHop] = make([]api.OnionApi, 0)
}
onions[firstHop] = append(onions[firstHop], api.OnionApi{
Onion: checkpointOnion,
From: c.Adddress,
To: firstHop,
})
c.status.AddSent(utils.GetLast(path), routingPath, dummyMsg)
}
}
}

if _, present := onions[addr]; !present {
onions[addr] = make([]api.OnionApi, 0)
}
onions[addr] = append(onions[msg.To], api.OnionApi{
onions[addr] = append(onions[addr], api.OnionApi{
Onion: onion,
From: c.Adddress,
To: addr,
Expand Down Expand Up @@ -300,7 +353,7 @@ func (c *Client) startRun(start api.StartRunApi) (bool, error) {
}

func (c *Client) Receive(o string) error {
if peeled, bruises, err := pi_t.PeelOnion(o, c.PrivateKey); err != nil {
if peeled, bruises, _, _, err := pi_t.PeelOnion(o, c.PrivateKey); err != nil {
return pl.WrapError(err, "node.Receive(): failed to remove layer")
} else {
slog.Info("Client received onion", "bruises", bruises, "from", peeled.LastHop, "to", peeled.NextHop, "layer", peeled.Layer, "is_checkpoint_onion", peeled.IsCheckpointOnion)
Expand Down
111 changes: 53 additions & 58 deletions internal/model/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ import (

// Node represents a node in the onion routing network
type Node struct {
ID int
Host string
Port int
PrivateKey string
PublicKey string
isMixer bool
mu sync.RWMutex
BulletinBoardUrl string
lastUpdate time.Time
status *api.NodeStatus
ID int
Host string
Port int
PrivateKey string
PublicKey string
isMixer bool
mu sync.RWMutex
BulletinBoardUrl string
lastUpdate time.Time
status *api.NodeStatus
checkpoints map[int]int
expectedCheckpoints map[int]int
}

// NewNode creates a new node
Expand All @@ -37,23 +39,16 @@ func NewNode(id int, host string, port int, bulletinBoardUrl string, isMixer boo
return nil, pl.WrapError(err, "node.NewClient(): failed to generate key pair")
} else {
n := &Node{
ID: id,
Host: host,
Port: port,
PublicKey: publicKey,
PrivateKey: privateKey,
BulletinBoardUrl: bulletinBoardUrl,
isMixer: isMixer,
status: &api.NodeStatus{
Received: make([]api.OnionStatus, 0),
Node: api.PublicNodeApi{
ID: id,
Address: fmt.Sprintf("http://%s:%d", host, port),
PublicKey: publicKey,
Time: time.Now(),
IsMixer: isMixer,
},
},
ID: id,
Host: host,
Port: port,
PublicKey: publicKey,
PrivateKey: privateKey,
BulletinBoardUrl: bulletinBoardUrl,
isMixer: isMixer,
status: api.NewNodeStatus(id, fmt.Sprintf("http://%s:%d", host, port), publicKey, isMixer),
checkpoints: make(map[int]int),
expectedCheckpoints: make(map[int]int),
}
if err2 := n.RegisterWithBulletinBoard(); err2 != nil {
return nil, pl.WrapError(err2, "node.NewNode(): failed to register with bulletin board")
Expand Down Expand Up @@ -103,42 +98,42 @@ func (n *Node) startRun(start api.StartRunApi) (didParticipate bool, e error) {
}

func (n *Node) Receive(o string) error {
if peeled, bruises, err := pi_t.PeelOnion(o, n.PrivateKey); err != nil {
if peeled, bruises, nonceVerification, expectCheckpoint, err := pi_t.PeelOnion(o, n.PrivateKey); err != nil {
return pl.WrapError(err, "node.Receive(): failed to remove layer")
} else {
n.mu.Lock()
defer n.mu.Unlock()

if peeled.NextHop == "" {
n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, true)
var msg api.Message
if err2 := json.Unmarshal([]byte(peeled.Payload), &msg); err2 != nil {
return pl.WrapError(err2, "node.Receive(): failed to unmarshal message")
}
slog.Info("Received message", "from", msg.From, "to", msg.To, "msg", msg.Msg)
if expectCheckpoint {
n.expectedCheckpoints[peeled.Layer]++
n.status.AddExpectedCheckpoint(peeled.Layer)
}
if peeled.IsCheckpointOnion {
n.checkpoints[peeled.Layer]++
n.status.AddCheckpointOnion(peeled.Layer)
}
slog.Info("Received onion", "layer", peeled.Layer, "destination", peeled.NextHop, "nonceVerification", nonceVerification, "expectCheckpoint", expectCheckpoint)

} else {
if !n.isMixer && bruises > config.GlobalConfig.MaxBruises {
n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, true)
slog.Info("Dropping onion", "layer", peeled.Layer, "destination", peeled.NextHop)
return nil
} else {
n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, false)
if !nonceVerification {
bruises += 1
}

if peeled.IsCheckpointOnion {
slog.Info("Received checkpoint onion", "layer", peeled.Layer, "destination", peeled.NextHop)
} else {
slog.Info("Received onion", "layer", peeled.Layer, "destination", peeled.NextHop)
}
//bruised, err2 := pi_t.BruiseOnion(payload)
//if err2 != nil {
// return pl.WrapError(err2, "node.Receive(): failed to bruise onion")
//}
addedHeader, err4 := pi_t.AddHeader(peeled, bruises, n.PrivateKey, n.PublicKey)
if err4 != nil {
return pl.WrapError(err4, "node.Receive(): failed to add header")
}
if err3 := sendToNode(peeled.NextHop, addedHeader); err != nil {
return pl.WrapError(err3, "node.Receive(): failed to send to next node")
}
if !n.isMixer && bruises > config.GlobalConfig.MaxBruises {

n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, true, nonceVerification, expectCheckpoint)
slog.Info("Dropping onion", "layer", peeled.Layer, "destination", peeled.NextHop)
return nil

} else if !peeled.IsCheckpointOnion {

n.status.AddOnion(peeled.LastHop, fmt.Sprintf("http://%s:%d", n.Host, n.Port), peeled.NextHop, peeled.Layer, peeled.IsCheckpointOnion, bruises, false, nonceVerification, expectCheckpoint)

addedHeader, err4 := pi_t.AddHeader(peeled, bruises, n.PrivateKey, n.PublicKey)
if err4 != nil {
return pl.WrapError(err4, "node.Receive(): failed to add header")
}
if err3 := sendToNode(peeled.NextHop, addedHeader); err != nil {
return pl.WrapError(err3, "node.Receive(): failed to send to next node")
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions internal/pi_t/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Directory: `/internal/pi_t`


- This directory contains the implementation of the Onion Routing protocol, `pi_t`. The key components are:
- This directory contains the implementation of several Onion Routing functions from Pi_t. The key components are:
- [`pi_t_functions.go`](pi_t_functions.go): Functions to form and peel onion layers.
- `keys`: HELPER package for key generation and encryption/decryption.
- `prf`: Package for Pseudo-Random Functions (PRF) used in the protocol.
- [`keys`](keys): Helper package for key generation and encryption/decryption.
- [`prf`](prf): Package for Pseudo-Random Functions (PRF) used in the protocol.

## Usage

Expand Down
Loading

0 comments on commit d45fa9b

Please sign in to comment.