Refactor: engine and peer decoupled, engine and eventbus now per peer

and stored top level in app. Storage has read only mode. Peer and group
state now event based and stored in profiles.
This commit is contained in:
Dan Ballard 2019-05-15 13:12:11 -07:00
parent 8be10930e7
commit 0c4bbe9ad1
17 changed files with 264 additions and 197 deletions

View File

@ -3,8 +3,10 @@ package app
import ( import (
"cwtch.im/cwtch/event" "cwtch.im/cwtch/event"
"cwtch.im/cwtch/peer" "cwtch.im/cwtch/peer"
"cwtch.im/cwtch/protocol/connections"
"cwtch.im/cwtch/storage" "cwtch.im/cwtch/storage"
"fmt" "fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/openprivacy/libricochet-go/log"
@ -16,12 +18,13 @@ import (
type application struct { type application struct {
peers map[string]peer.CwtchPeer peers map[string]peer.CwtchPeer
storage map[string]storage.ProfileStore
engines map[string]connections.Engine
eventBuses map[string]*event.Manager
acn connectivity.ACN acn connectivity.ACN
directory string directory string
mutex sync.Mutex mutex sync.Mutex
primaryonion string primaryonion string
storage map[string]storage.ProfileStore
eventBus *event.Manager
} }
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers // Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
@ -32,20 +35,18 @@ type Application interface {
PrimaryIdentity() peer.CwtchPeer PrimaryIdentity() peer.CwtchPeer
GetPeer(onion string) peer.CwtchPeer GetPeer(onion string) peer.CwtchPeer
ListPeers() map[string]string ListPeers() map[string]string
GetEventBus(onion string) *event.Manager
LaunchPeers() LaunchPeers()
EventBus() *event.Manager ShutdownPeer(string)
Shutdown() Shutdown()
} }
// NewApp creates a new app with some environment awareness and initializes a Tor Manager // NewApp creates a new app with some environment awareness and initializes a Tor Manager
func NewApp(acn connectivity.ACN, appDirectory string) Application { func NewApp(acn connectivity.ACN, appDirectory string) Application {
log.Debugf("NewApp(%v)\n", appDirectory) log.Debugf("NewApp(%v)\n", appDirectory)
app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), directory: appDirectory, acn: acn} app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), eventBuses: make(map[string]*event.Manager), directory: appDirectory, acn: acn}
os.Mkdir(path.Join(app.directory, "profiles"), 0700) os.Mkdir(path.Join(app.directory, "profiles"), 0700)
app.eventBus = new(event.Manager)
app.eventBus.Initialize()
return app return app
} }
@ -53,19 +54,31 @@ func NewApp(acn connectivity.ACN, appDirectory string) Application {
func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer, error) { func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer, error) {
log.Debugf("CreatePeer(%v)\n", name) log.Debugf("CreatePeer(%v)\n", name)
eventBus := new(event.Manager)
eventBus.Initialize()
profile := storage.NewProfile(name) profile := storage.NewProfile(name)
profileStore := storage.NewProfileStore(app.eventBus, path.Join(app.directory, "profiles", profile.LocalID), password, profile) profileStore := storage.NewProfileWriterStore(eventBus, path.Join(app.directory, "profiles", profile.LocalID), password, profile)
pc := profileStore.GetProfileCopy() pc := profileStore.GetProfileCopy()
p := peer.FromProfile(pc) p := peer.FromProfile(pc)
p.Init(app.acn, app.eventBus)
_, exists := app.peers[p.GetProfile().Onion] _, exists := app.peers[p.GetProfile().Onion]
if exists { if exists {
p.Shutdown() profileStore.Shutdown()
eventBus.Shutdown()
return nil, fmt.Errorf("Error: profile for onion %v already exists", p.GetProfile().Onion) return nil, fmt.Errorf("Error: profile for onion %v already exists", p.GetProfile().Onion)
} }
p.Init(app.acn, eventBus)
blockedPeers := profile.BlockedPeers()
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
identity := identity.InitializeV3(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, eventBus, blockedPeers)
app.mutex.Lock() app.mutex.Lock()
app.peers[p.GetProfile().Onion] = p app.peers[p.GetProfile().Onion] = p
app.storage[p.GetProfile().Onion] = profileStore app.storage[p.GetProfile().Onion] = profileStore
app.engines[p.GetProfile().Onion] = engine
app.eventBuses[p.GetProfile().Onion] = eventBus
app.mutex.Unlock() app.mutex.Unlock()
return p, nil return p, nil
@ -79,8 +92,10 @@ func (app *application) LoadProfiles(password string) error {
for _, file := range files { for _, file := range files {
// TODO: Per profile eventBus eventBus := new(event.Manager)
profileStore := storage.NewProfileStore(app.eventBus, path.Join(app.directory, "profiles", file.Name()), password, nil) eventBus.Initialize()
profileStore := storage.NewProfileWriterStore(eventBus, path.Join(app.directory, "profiles", file.Name()), password, nil)
err = profileStore.Load() err = profileStore.Load()
if err != nil { if err != nil {
continue continue
@ -91,16 +106,23 @@ func (app *application) LoadProfiles(password string) error {
_, exists := app.peers[profile.Onion] _, exists := app.peers[profile.Onion]
if exists { if exists {
profileStore.Shutdown() profileStore.Shutdown()
eventBus.Shutdown()
log.Errorf("profile for onion %v already exists", profile.Onion) log.Errorf("profile for onion %v already exists", profile.Onion)
continue continue
} }
peer := peer.FromProfile(profile) peer := peer.FromProfile(profile)
peer.Init(app.acn, app.eventBus) peer.Init(app.acn, eventBus)
blockedPeers := profile.BlockedPeers()
identity := identity.InitializeV3(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, eventBus, blockedPeers)
app.mutex.Lock() app.mutex.Lock()
app.peers[profile.Onion] = peer app.peers[profile.Onion] = peer
app.storage[profile.Onion] = profileStore app.storage[profile.Onion] = profileStore
app.engines[profile.Onion] = engine
app.eventBuses[profile.Onion] = eventBus
if app.primaryonion == "" { if app.primaryonion == "" {
app.primaryonion = profile.Onion app.primaryonion = profile.Onion
} }
@ -109,10 +131,13 @@ func (app *application) LoadProfiles(password string) error {
return nil return nil
} }
// LaunchPeers starts each peer Listening and connecting to peers and groups
func (app *application) LaunchPeers() { func (app *application) LaunchPeers() {
for _, p := range app.peers { for _, p := range app.peers {
if !p.IsStarted() { if !p.IsStarted() {
p.Listen() p.Listen()
p.StartPeersConnections()
p.StartGroupConnections()
} }
} }
} }
@ -139,20 +164,34 @@ func (app *application) GetPeer(onion string) peer.CwtchPeer {
return nil return nil
} }
/* // GetEventBus returns a cwtchPeer's event bus
// GetTorStatus returns tor control port bootstrap-phase status info in a map func (app *application) GetEventBus(onion string) *event.Manager {
func (app *application) GetTorStatus() (map[string]string, error) { if manager, ok := app.eventBuses[onion]; ok {
return app.torManager.GetStatus() return manager
}*/ }
return nil
}
// Fetch the app's event manager // ShutdownPeer shuts down a peer and removes it from the app's management
func (app *application) EventBus() *event.Manager { func (app *application) ShutdownPeer(onion string) {
return app.eventBus app.mutex.Lock()
defer app.mutex.Unlock()
app.eventBuses[onion].Shutdown()
delete(app.eventBuses, onion)
app.peers[onion].Shutdown()
delete(app.peers, onion)
app.engines[onion].Shutdown()
delete(app.engines, onion)
app.storage[onion].Shutdown()
delete(app.storage, onion)
} }
// Shutdown shutsdown all peers of an app and then the tormanager // Shutdown shutsdown all peers of an app and then the tormanager
func (app *application) Shutdown() { func (app *application) Shutdown() {
for _, peer := range app.peers { for id, peer := range app.peers {
peer.Shutdown() peer.Shutdown()
app.engines[id].Shutdown()
app.storage[id].Shutdown()
app.eventBuses[id].Shutdown()
} }
} }

View File

@ -11,16 +11,16 @@ import (
"time" "time"
) )
func waitForPeerServerConnection(peer peer.CwtchPeer, server string) error { func waitForPeerGroupConnection(peer peer.CwtchPeer, groupID string) error {
for { for {
servers := peer.GetServers() _, ok := peer.GetProfile().Groups[groupID]
state, ok := servers[server]
if ok { if ok {
state := peer.GetGroupState(groupID)
if state == connections.FAILED { if state == connections.FAILED {
return errors.New("Connection to server " + server + " failed!") return errors.New("Connection to group " + groupID + " failed!")
} }
if state != connections.AUTHENTICATED { if state != connections.AUTHENTICATED {
fmt.Printf("peer %v waiting to authenticate with server %v, current state: %v\n", peer.GetProfile().Onion, server, connections.ConnectionStateName[state]) fmt.Printf("peer %v waiting to authenticate with group %v 's server, current state: %v\n", peer.GetProfile().Onion, groupID, connections.ConnectionStateName[state])
time.Sleep(time.Second * 10) time.Sleep(time.Second * 10)
continue continue
} }
@ -57,7 +57,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
err = waitForPeerServerConnection(botPeer, serverAddr) err = waitForPeerGroupConnection(botPeer, groupID)
if err != nil { if err != nil {
fmt.Printf("Could not connect to server %v: %v\n", serverAddr, err) fmt.Printf("Could not connect to server %v: %v\n", serverAddr, err)
os.Exit(1) os.Exit(1)

View File

@ -6,7 +6,6 @@ import (
"bytes" "bytes"
"cwtch.im/cwtch/model" "cwtch.im/cwtch/model"
"cwtch.im/cwtch/protocol/connections"
"fmt" "fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/openprivacy/libricochet-go/log"
@ -45,8 +44,8 @@ var suggestionsSelectedProfile = []prompt.Suggest{
{Text: "/invite", Description: "invite a new contact"}, {Text: "/invite", Description: "invite a new contact"},
{Text: "/invite-to-group", Description: "invite an existing contact to join an existing group"}, {Text: "/invite-to-group", Description: "invite an existing contact to join an existing group"},
{Text: "/accept-invite", Description: "accept the invite of a group"}, {Text: "/accept-invite", Description: "accept the invite of a group"},
{Text: "/list-servers", Description: "retrieve a list of servers and their connection status"}, /*{Text: "/list-servers", Description: "retrieve a list of servers and their connection status"},
{Text: "/list-peers", Description: "retrieve a list of peers and their connection status"}, {Text: "/list-peers", Description: "retrieve a list of peers and their connection status"},*/
{Text: "/export-group", Description: "export a group invite: prints as a string"}, {Text: "/export-group", Description: "export a group invite: prints as a string"},
{Text: "/trust", Description: "trust a peer"}, {Text: "/trust", Description: "trust a peer"},
{Text: "/block", Description: "block a peer - you will no longer see messages or connect to this peer"}, {Text: "/block", Description: "block a peer - you will no longer see messages or connect to this peer"},
@ -55,13 +54,13 @@ var suggestionsSelectedProfile = []prompt.Suggest{
var suggestions = suggestionsBase var suggestions = suggestionsBase
var usages = map[string]string{ var usages = map[string]string{
"/new-profile": "/new-profile [name]", "/new-profile": "/new-profile [name]",
"/load-profiles": "/load-profiles", "/load-profiles": "/load-profiles",
"/list-profiles": "", "/list-profiles": "",
"/select-profile": "/select-profile [onion]", "/select-profile": "/select-profile [onion]",
"/quit": "", "/quit": "",
"/list-servers": "", /* "/list-servers": "",
"/list-peers": "", "/list-peers": "",*/
"/list-contacts": "", "/list-contacts": "",
"/list-groups": "", "/list-groups": "",
"/select-group": "/select-group [groupid]", "/select-group": "/select-group [groupid]",
@ -424,7 +423,7 @@ func main() {
} else { } else {
fmt.Printf("Error inviting peer, usage: %s\n", usages[commands[0]]) fmt.Printf("Error inviting peer, usage: %s\n", usages[commands[0]])
} }
case "/list-peers": /*case "/list-peers":
peers := peer.GetPeers() peers := peer.GetPeers()
for p, s := range peers { for p, s := range peers {
fmt.Printf("Name: %v Status: %v\n", p, connections.ConnectionStateName[s]) fmt.Printf("Name: %v Status: %v\n", p, connections.ConnectionStateName[s])
@ -433,7 +432,7 @@ func main() {
servers := peer.GetServers() servers := peer.GetServers()
for s, st := range servers { for s, st := range servers {
fmt.Printf("Name: %v Status: %v\n", s, connections.ConnectionStateName[st]) fmt.Printf("Name: %v Status: %v\n", s, connections.ConnectionStateName[st])
} }*/
case "/list-contacts": case "/list-contacts":
contacts := peer.GetContacts() contacts := peer.GetContacts()
for _, onion := range contacts { for _, onion := range contacts {

View File

@ -84,6 +84,14 @@ const (
// Key [eg "nick"] // Key [eg "nick"]
// Data [eg "open privacy board"] // Data [eg "open privacy board"]
SetGroupAttribute = Type("SetGroupAttribute") SetGroupAttribute = Type("SetGroupAttribute")
// RemotePeer
// ConnectionState
PeerStateChange = Type("PeerStateChange")
// GroupServer
// ConnectionState
ServerStateChange = Type("GroupStateChange")
) )
// Field defines common event attributes // Field defines common event attributes
@ -106,6 +114,8 @@ const (
ProfileName = Field("ProfileName") ProfileName = Field("ProfileName")
ConnectionState = Field("ConnectionState")
Key = Field("Key") Key = Field("Key")
Data = Field("Data") Data = Field("Data")

View File

@ -24,6 +24,7 @@ type Manager struct {
events chan Event events chan Event
mapMutex sync.Mutex mapMutex sync.Mutex
internal chan bool internal chan bool
closed bool
} }
// Initialize sets up the Manager. // Initialize sets up the Manager.
@ -31,6 +32,7 @@ func (em *Manager) Initialize() {
em.subscribers = make(map[Type][]chan Event) em.subscribers = make(map[Type][]chan Event)
em.events = make(chan Event) em.events = make(chan Event)
em.internal = make(chan bool) em.internal = make(chan bool)
em.closed = false
go em.eventBus() go em.eventBus()
} }
@ -44,7 +46,7 @@ func (em *Manager) Subscribe(eventType Type, eventChannel chan Event) {
// Publish takes an Event and sends it to the internal eventBus where it is distributed to all Subscribers // Publish takes an Event and sends it to the internal eventBus where it is distributed to all Subscribers
func (em *Manager) Publish(event Event) { func (em *Manager) Publish(event Event) {
if event.EventType != "" { if event.EventType != "" && em.closed != true {
em.events <- event em.events <- event
} }
} }
@ -83,6 +85,7 @@ func (em *Manager) eventBus() {
// Shutdown triggers, and waits for, the internal eventBus goroutine to finish // Shutdown triggers, and waits for, the internal eventBus goroutine to finish
func (em *Manager) Shutdown() { func (em *Manager) Shutdown() {
em.events <- Event{} em.events <- Event{}
em.closed = true
// wait for eventBus to finish // wait for eventBus to finish
<-em.internal <-em.internal
close(em.events) close(em.events)

View File

@ -29,6 +29,7 @@ type Group struct {
Attributes map[string]string Attributes map[string]string
lock sync.Mutex lock sync.Mutex
LocalID string LocalID string
State string `json:"-"`
unacknowledgedMessages []Message unacknowledgedMessages []Message
} }

View File

@ -27,6 +27,7 @@ type PublicProfile struct {
Attributes map[string]string Attributes map[string]string
Timeline Timeline Timeline Timeline
LocalID string // used by storage engine LocalID string // used by storage engine
State string `json:"-"`
lock sync.Mutex lock sync.Mutex
} }
@ -186,6 +187,18 @@ func (p *Profile) BlockPeer(onion string) (err error) {
return return
} }
// BlockedPeers calculates a list of Peers who have been Blocked.
func (p *Profile) BlockedPeers() []string {
blockedPeers := []string{}
for _, contact := range p.GetContacts() {
c, _ := p.GetContact(contact)
if c.Blocked {
blockedPeers = append(blockedPeers, c.Onion)
}
}
return blockedPeers
}
// TrustPeer sets a contact to trusted // TrustPeer sets a contact to trusted
func (p *Profile) TrustPeer(onion string) (err error) { func (p *Profile) TrustPeer(onion string) (err error) {
p.lock.Lock() p.lock.Lock()

View File

@ -5,11 +5,11 @@ import (
"cwtch.im/cwtch/model" "cwtch.im/cwtch/model"
"cwtch.im/cwtch/protocol" "cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/protocol/connections" "cwtch.im/cwtch/protocol/connections"
"encoding/base32"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/openprivacy/libricochet-go/log"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"strings" "strings"
@ -24,7 +24,6 @@ type cwtchPeer struct {
shutdown bool shutdown bool
started bool started bool
engine connections.Engine
queue *event.Queue queue *event.Queue
eventBus *event.Manager eventBus *event.Manager
} }
@ -47,9 +46,7 @@ type CwtchPeer interface {
SendMessageToGroupTracked(string, string) (string, error) SendMessageToGroupTracked(string, string) (string, error)
GetProfile() *model.Profile GetProfile() *model.Profile
GetPeerState(string) connections.ConnectionState
GetPeers() map[string]connections.ConnectionState
GetServers() map[string]connections.ConnectionState
StartGroup(string) (string, []byte, error) StartGroup(string) (string, []byte, error)
@ -57,13 +54,16 @@ type CwtchPeer interface {
ExportGroup(string) (string, error) ExportGroup(string) (string, error)
GetGroup(string) *model.Group GetGroup(string) *model.Group
GetGroupState(string) connections.ConnectionState
GetGroups() []string GetGroups() []string
AddContact(nick, onion string, publickey []byte, trusted bool) AddContact(nick, onion string, trusted bool)
GetContacts() []string GetContacts() []string
GetContact(string) *model.PublicProfile GetContact(string) *model.PublicProfile
IsStarted() bool IsStarted() bool
Listen() Listen()
StartPeersConnections()
StartGroupConnections()
Shutdown() Shutdown()
} }
@ -90,19 +90,8 @@ func (cp *cwtchPeer) Init(acn connectivity.ACN, eventBus *event.Manager) {
cp.eventBus = eventBus cp.eventBus = eventBus
cp.eventBus.Subscribe(event.EncryptedGroupMessage, cp.queue.EventChannel) cp.eventBus.Subscribe(event.EncryptedGroupMessage, cp.queue.EventChannel)
cp.eventBus.Subscribe(event.NewGroupInvite, cp.queue.EventChannel) cp.eventBus.Subscribe(event.NewGroupInvite, cp.queue.EventChannel)
cp.eventBus.Subscribe(event.ServerStateChange, cp.queue.EventChannel)
// Calculate a list of Peers who have been Blocked. cp.eventBus.Subscribe(event.PeerStateChange, cp.queue.EventChannel)
blockedPeers := []string{}
for _, contact := range cp.Profile.GetContacts() {
c, _ := cp.Profile.GetContact(contact)
if c.Blocked {
blockedPeers = append(blockedPeers, c.Onion)
}
}
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
identity := identity.InitializeV3(cp.Profile.Name, &cp.Profile.Ed25519PrivateKey, &cp.Profile.Ed25519PublicKey)
cp.engine = connections.NewProtocolEngine(identity, cp.Profile.Ed25519PrivateKey, acn, eventBus, blockedPeers)
} }
// ImportGroup intializes a group from an imported source rather than a peer invite // ImportGroup intializes a group from an imported source rather than a peer invite
@ -175,8 +164,9 @@ func (cp *cwtchPeer) GetGroup(groupID string) *model.Group {
return cp.Profile.GetGroupByGroupID(groupID) return cp.Profile.GetGroupByGroupID(groupID)
} }
func (cp *cwtchPeer) AddContact(nick, onion string, publickey []byte, trusted bool) { func (cp *cwtchPeer) AddContact(nick, onion string, trusted bool) {
pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: publickey, Trusted: trusted, Blocked: false, Onion: onion, Attributes: map[string]string{"nick": nick}} decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: decodedPub, Trusted: trusted, Blocked: false, Onion: onion, Attributes: map[string]string{"nick": nick}}
cp.Profile.AddContact(onion, pp) cp.Profile.AddContact(onion, pp)
pd, _ := json.Marshal(pp) pd, _ := json.Marshal(pp)
cp.eventBus.Publish(event.NewEvent(event.PeerCreated, map[event.Field]string{ cp.eventBus.Publish(event.NewEvent(event.PeerCreated, map[event.Field]string{
@ -201,6 +191,14 @@ func (cp *cwtchPeer) GetProfile() *model.Profile {
return cp.Profile return cp.Profile
} }
func (cp *cwtchPeer) GetPeerState(onion string) connections.ConnectionState {
return connections.ConnectionStateType[cp.Profile.Contacts[onion].State]
}
func (cp *cwtchPeer) GetGroupState(groupid string) connections.ConnectionState {
return connections.ConnectionStateType[cp.Profile.Groups[groupid].State]
}
// PeerWithOnion is the entry point for cwtchPeer relationships // PeerWithOnion is the entry point for cwtchPeer relationships
func (cp *cwtchPeer) PeerWithOnion(onion string) *connections.PeerPeerConnection { func (cp *cwtchPeer) PeerWithOnion(onion string) *connections.PeerPeerConnection {
cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion})) cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion}))
@ -255,16 +253,6 @@ func (cp *cwtchPeer) SendMessageToPeer(onion string, message string) string {
return event.EventID return event.EventID
} }
// GetPeers returns a list of peer connections.
func (cp *cwtchPeer) GetPeers() map[string]connections.ConnectionState {
return cp.engine.GetPeers()
}
// GetServers returns a list of server connections
func (cp *cwtchPeer) GetServers() map[string]connections.ConnectionState {
return cp.engine.GetServers()
}
// TrustPeer sets an existing peer relationship to trusted // TrustPeer sets an existing peer relationship to trusted
func (cp *cwtchPeer) TrustPeer(peer string) error { func (cp *cwtchPeer) TrustPeer(peer string) error {
err := cp.Profile.TrustPeer(peer) err := cp.Profile.TrustPeer(peer)
@ -298,14 +286,34 @@ func (cp *cwtchPeer) RejectInvite(groupID string) {
cp.Profile.RejectInvite(groupID) cp.Profile.RejectInvite(groupID)
} }
// Listen makes the peer open a listening port to accept incoming connections (and be detactably online)
func (cp *cwtchPeer) Listen() { func (cp *cwtchPeer) Listen() {
cp.eventBus.Publish(event.NewEvent(event.ProtocolEngineStartListen, map[event.Field]string{})) cp.eventBus.Publish(event.NewEvent(event.ProtocolEngineStartListen, map[event.Field]string{}))
} }
// StartGroupConnections attempts to connect to all group servers (thus initiating reconnect attempts in the conectionsmanager)
func (cp *cwtchPeer) StartPeersConnections() {
for _, contact := range cp.GetContacts() {
cp.PeerWithOnion(contact)
}
}
// StartPeerConnections attempts to connect to all peers (thus initiating reconnect attempts in the conectionsmanager)
func (cp *cwtchPeer) StartGroupConnections() {
joinedServers := map[string]bool{}
for _, groupID := range cp.GetGroups() {
// Only send a join server packet if we haven't joined this server yet...
group := cp.GetGroup(groupID)
if joined := joinedServers[groupID]; group.Accepted && !joined {
cp.JoinServer(group.GroupServer)
joinedServers[group.GroupServer] = true
}
}
}
// Shutdown kills all connections and cleans up all goroutines for the peer // Shutdown kills all connections and cleans up all goroutines for the peer
func (cp *cwtchPeer) Shutdown() { func (cp *cwtchPeer) Shutdown() {
cp.shutdown = true cp.shutdown = true
cp.engine.Shutdown()
cp.queue.Shutdown() cp.queue.Shutdown()
} }
@ -328,6 +336,16 @@ func (cp *cwtchPeer) eventHandler() {
var groupInvite protocol.GroupChatInvite var groupInvite protocol.GroupChatInvite
proto.Unmarshal([]byte(ev.Data[event.GroupInvite]), &groupInvite) proto.Unmarshal([]byte(ev.Data[event.GroupInvite]), &groupInvite)
cp.Profile.ProcessInvite(&groupInvite, ev.Data[event.RemotePeer]) cp.Profile.ProcessInvite(&groupInvite, ev.Data[event.RemotePeer])
case event.PeerStateChange:
if _, exists := cp.Profile.Contacts[ev.Data[event.RemotePeer]]; exists {
cp.Profile.Contacts[ev.Data[event.RemotePeer]].State = ev.Data[event.ConnectionState]
}
case event.ServerStateChange:
for _, group := range cp.Profile.Groups {
if group.GroupServer == ev.Data[event.GroupServer] {
group.State = ev.Data[event.ConnectionState]
}
}
default: default:
if ev.EventType != "" { if ev.EventType != "" {
log.Errorf("peer event handler received an event it was not subscribed for: %v", ev.EventType) log.Errorf("peer event handler received an event it was not subscribed for: %v", ev.EventType)

View File

@ -42,12 +42,12 @@ func (m *Manager) ManagePeerConnection(host string, engine Engine) *PeerPeerConn
} }
// ManageServerConnection creates a new ServerConnection for Host with the given callback handler. // ManageServerConnection creates a new ServerConnection for Host with the given callback handler.
func (m *Manager) ManageServerConnection(host string, messageHandler func(string, *protocol.GroupMessage), closedHandler func(string)) { func (m *Manager) ManageServerConnection(host string, engine Engine, messageHandler func(string, *protocol.GroupMessage), closedHandler func(string)) {
m.lock.Lock() m.lock.Lock()
psc, exists := m.serverConnections[host] psc, exists := m.serverConnections[host]
newPsc := NewPeerServerConnection(m.acn, host) newPsc := NewPeerServerConnection(engine, host)
newPsc.GroupMessageHandler = messageHandler newPsc.GroupMessageHandler = messageHandler
newPsc.CloseHandler = closedHandler newPsc.CloseHandler = closedHandler
go newPsc.Run() go newPsc.Run()
@ -60,28 +60,6 @@ func (m *Manager) ManageServerConnection(host string, messageHandler func(string
m.lock.Unlock() m.lock.Unlock()
} }
// GetPeers returns a map of all peer connections with their state
func (m *Manager) GetPeers() map[string]ConnectionState {
rm := make(map[string]ConnectionState)
m.lock.Lock()
for onion, ppc := range m.peerConnections {
rm[onion] = ppc.GetState()
}
m.lock.Unlock()
return rm
}
// GetServers returns a map of all server connections with their state.
func (m *Manager) GetServers() map[string]ConnectionState {
rm := make(map[string]ConnectionState)
m.lock.Lock()
for onion, psc := range m.serverConnections {
rm[onion] = psc.GetState()
}
m.lock.Unlock()
return rm
}
// GetPeerPeerConnectionForOnion safely returns a given peer connection // GetPeerPeerConnectionForOnion safely returns a given peer connection
func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) { func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) {
m.lock.Lock() m.lock.Lock()
@ -103,7 +81,6 @@ func (m *Manager) AttemptReconnections() {
maxTimeout := time.Minute * 5 maxTimeout := time.Minute * 5
// nearly instant first run, next few runs will prolly be too quick to have any FAILED and will gracefully slow to MAX after that // nearly instant first run, next few runs will prolly be too quick to have any FAILED and will gracefully slow to MAX after that
timeout := time.Millisecond * 500 timeout := time.Millisecond * 500
for { for {
select { select {
case <-time.After(timeout): case <-time.After(timeout):

View File

@ -45,6 +45,7 @@ type engine struct {
type Engine interface { type Engine interface {
Identity() identity.Identity Identity() identity.Identity
ACN() connectivity.ACN ACN() connectivity.ACN
EventManager() *event.Manager
GetPeerHandler(string) *CwtchPeerHandler GetPeerHandler(string) *CwtchPeerHandler
ContactRequest(string, string) string ContactRequest(string, string) string
@ -52,9 +53,6 @@ type Engine interface {
LookupContact(string, rsa.PublicKey) (bool, bool) LookupContact(string, rsa.PublicKey) (bool, bool)
LookupContactV3(string, ed25519.PublicKey) (bool, bool) LookupContactV3(string, ed25519.PublicKey) (bool, bool)
GetPeers() map[string]ConnectionState
GetServers() map[string]ConnectionState
Shutdown() Shutdown()
} }
@ -94,6 +92,10 @@ func (e *engine) Identity() identity.Identity {
return e.identity return e.identity
} }
func (e *engine) EventManager() *event.Manager {
return e.eventManager
}
// eventHandler process events from other subsystems // eventHandler process events from other subsystems
func (e *engine) eventHandler() { func (e *engine) eventHandler() {
for { for {
@ -244,7 +246,7 @@ func (e *engine) finishedFetch(server string) {
// joinServer manages a new server connection with the given onion address // joinServer manages a new server connection with the given onion address
func (e *engine) joinServer(onion string) { func (e *engine) joinServer(onion string) {
e.connectionsManager.ManageServerConnection(onion, e.receiveGroupMessage, e.finishedFetch) e.connectionsManager.ManageServerConnection(onion, e, e.receiveGroupMessage, e.finishedFetch)
} }
// sendMessageToGroup attempts to sent the given message to the given group id. // sendMessageToGroup attempts to sent the given message to the given group id.
@ -264,16 +266,6 @@ func (e *engine) sendMessageToGroup(server string, ct []byte, sig []byte) {
} }
} }
// GetPeers returns a list of peer connections.
func (e *engine) GetPeers() map[string]ConnectionState {
return e.connectionsManager.GetPeers()
}
// GetServers returns a list of server connections
func (e *engine) GetServers() map[string]ConnectionState {
return e.connectionsManager.GetServers()
}
// CwtchPeerInstance encapsulates incoming peer connections // CwtchPeerInstance encapsulates incoming peer connections
type CwtchPeerInstance struct { type CwtchPeerInstance struct {
rai *application.Instance rai *application.Instance

View File

@ -1,6 +1,7 @@
package connections package connections
import ( import (
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/protocol/connections/peer" "cwtch.im/cwtch/protocol/connections/peer"
"errors" "errors"
"git.openprivacy.ca/openprivacy/libricochet-go" "git.openprivacy.ca/openprivacy/libricochet-go"
@ -28,6 +29,14 @@ func NewPeerPeerConnection(peerhostname string, protocolEngine Engine) *PeerPeer
return ppc return ppc
} }
func (ppc *PeerPeerConnection) setState(state ConnectionState) {
ppc.state = state
ppc.protocolEngine.EventManager().Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(ppc.PeerHostname),
event.ConnectionState: ConnectionStateName[state],
}))
}
// GetState returns the current connection state // GetState returns the current connection state
func (ppc *PeerPeerConnection) GetState() ConnectionState { func (ppc *PeerPeerConnection) GetState() ConnectionState {
return ppc.state return ppc.state
@ -78,14 +87,14 @@ func (ppc *PeerPeerConnection) WaitTilAuthenticated() {
// Run manages the setup and teardown of a peer->peer connection // Run manages the setup and teardown of a peer->peer connection
func (ppc *PeerPeerConnection) Run() error { func (ppc *PeerPeerConnection) Run() error {
ppc.state = CONNECTING ppc.setState(CONNECTING)
rc, err := goricochet.Open(ppc.protocolEngine.ACN(), ppc.PeerHostname) rc, err := goricochet.Open(ppc.protocolEngine.ACN(), ppc.PeerHostname)
if err == nil { if err == nil {
ppc.connection = rc ppc.connection = rc
ppc.state = CONNECTED ppc.setState(CONNECTED)
_, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsV3Client(ppc.protocolEngine.Identity()) _, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsV3Client(ppc.protocolEngine.Identity())
if err == nil { if err == nil {
ppc.state = AUTHENTICATED ppc.setState(AUTHENTICATED)
go func() { go func() {
ppc.connection.Do(func() error { ppc.connection.Do(func() error {
ppc.connection.RequestOpenChannel("im.cwtch.peer", &peer.CwtchPeerChannel{Handler: ppc.protocolEngine.GetPeerHandler(ppc.PeerHostname)}) ppc.connection.RequestOpenChannel("im.cwtch.peer", &peer.CwtchPeerChannel{Handler: ppc.protocolEngine.GetPeerHandler(ppc.PeerHostname)})
@ -101,13 +110,13 @@ func (ppc *PeerPeerConnection) Run() error {
ppc.connection.Process(ppc) ppc.connection.Process(ppc)
} }
} }
ppc.state = FAILED ppc.setState(FAILED)
return err return err
} }
// Close closes the connection // Close closes the connection
func (ppc *PeerPeerConnection) Close() { func (ppc *PeerPeerConnection) Close() {
ppc.state = KILLED ppc.setState(KILLED)
if ppc.connection != nil { if ppc.connection != nil {
ppc.connection.Close() ppc.connection.Close()
} }

View File

@ -2,6 +2,7 @@ package connections
import ( import (
"crypto/rand" "crypto/rand"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/protocol" "cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/protocol/connections/fetch" "cwtch.im/cwtch/protocol/connections/fetch"
"cwtch.im/cwtch/protocol/connections/listen" "cwtch.im/cwtch/protocol/connections/listen"
@ -10,7 +11,6 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go" "git.openprivacy.ca/openprivacy/libricochet-go"
"git.openprivacy.ca/openprivacy/libricochet-go/channels" "git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connection" "git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/identity" "git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/openprivacy/libricochet-go/log"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
@ -20,19 +20,19 @@ import (
// PeerServerConnection encapsulates a single Peer->Server connection // PeerServerConnection encapsulates a single Peer->Server connection
type PeerServerConnection struct { type PeerServerConnection struct {
connection.AutoConnectionHandler connection.AutoConnectionHandler
Server string Server string
state ConnectionState state ConnectionState
connection *connection.Connection connection *connection.Connection
acn connectivity.ACN protocolEngine Engine
GroupMessageHandler func(string, *protocol.GroupMessage) GroupMessageHandler func(string, *protocol.GroupMessage)
CloseHandler func(string) CloseHandler func(string)
} }
// NewPeerServerConnection creates a new Peer->Server outbound connection // NewPeerServerConnection creates a new Peer->Server outbound connection
func NewPeerServerConnection(acn connectivity.ACN, serverhostname string) *PeerServerConnection { func NewPeerServerConnection(engine Engine, serverhostname string) *PeerServerConnection {
psc := new(PeerServerConnection) psc := new(PeerServerConnection)
psc.acn = acn psc.protocolEngine = engine
psc.Server = serverhostname psc.Server = serverhostname
psc.Init() psc.Init()
return psc return psc
@ -43,6 +43,14 @@ func (psc *PeerServerConnection) GetState() ConnectionState {
return psc.state return psc.state
} }
func (psc *PeerServerConnection) setState(state ConnectionState) {
psc.state = state
psc.protocolEngine.EventManager().Publish(event.NewEvent(event.ServerStateChange, map[event.Field]string{
event.GroupServer: string(psc.Server),
event.ConnectionState: ConnectionStateName[state],
}))
}
// WaitTilAuthenticated waits until the underlying connection is authenticated // WaitTilAuthenticated waits until the underlying connection is authenticated
func (psc *PeerServerConnection) WaitTilAuthenticated() { func (psc *PeerServerConnection) WaitTilAuthenticated() {
for { for {
@ -56,15 +64,15 @@ func (psc *PeerServerConnection) WaitTilAuthenticated() {
// Run manages the setup and teardown of a peer server connection // Run manages the setup and teardown of a peer server connection
func (psc *PeerServerConnection) Run() error { func (psc *PeerServerConnection) Run() error {
log.Infof("Connecting to %v", psc.Server) log.Infof("Connecting to %v", psc.Server)
rc, err := goricochet.Open(psc.acn, psc.Server) rc, err := goricochet.Open(psc.protocolEngine.ACN(), psc.Server)
if err == nil { if err == nil {
psc.connection = rc psc.connection = rc
psc.state = CONNECTED psc.setState(CONNECTED)
pub, priv, err := ed25519.GenerateKey(rand.Reader) pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err == nil { if err == nil {
_, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsV3Client(identity.InitializeV3("cwtchpeer", &priv, &pub)) _, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsV3Client(identity.InitializeV3("cwtchpeer", &priv, &pub))
if err == nil { if err == nil {
psc.state = AUTHENTICATED psc.setState(AUTHENTICATED)
go func() { go func() {
psc.connection.Do(func() error { psc.connection.Do(func() error {
@ -81,7 +89,7 @@ func (psc *PeerServerConnection) Run() error {
} }
} }
} }
psc.state = FAILED psc.setState(FAILED)
return err return err
} }
@ -128,7 +136,7 @@ func (psc *PeerServerConnection) SendGroupMessage(gm *protocol.GroupMessage) err
// Close shuts down the connection (freeing the handler goroutines) // Close shuts down the connection (freeing the handler goroutines)
func (psc *PeerServerConnection) Close() { func (psc *PeerServerConnection) Close() {
psc.state = KILLED psc.setState(KILLED)
if psc.connection != nil { if psc.connection != nil {
psc.connection.Close() psc.connection.Close()
} }

View File

@ -2,6 +2,7 @@ package connections
import ( import (
"crypto/rand" "crypto/rand"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/protocol" "cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/server/fetch" "cwtch.im/cwtch/server/fetch"
"cwtch.im/cwtch/server/send" "cwtch.im/cwtch/server/send"
@ -75,7 +76,11 @@ func TestPeerServerConnection(t *testing.T) {
<-listenChan <-listenChan
onionAddr := identity.Hostname() onionAddr := identity.Hostname()
psc := NewPeerServerConnection(connectivity.LocalProvider(), "127.0.0.1:5451|"+onionAddr) manager := &event.Manager{}
manager.Initialize()
engine := NewProtocolEngine(identity, priv, connectivity.LocalProvider(), manager, nil)
psc := NewPeerServerConnection(engine, "127.0.0.1:5451|"+onionAddr)
numcalls := 0 numcalls := 0
psc.GroupMessageHandler = func(s string, gm *protocol.GroupMessage) { psc.GroupMessageHandler = func(s string, gm *protocol.GroupMessage) {
numcalls++ numcalls++

View File

@ -7,7 +7,7 @@ type ConnectionState int
// DISCONNECTED - No existing connection has been made, or all attempts have failed // DISCONNECTED - No existing connection has been made, or all attempts have failed
// CONNECTING - We are in the process of attempting to connect to a given endpoint // CONNECTING - We are in the process of attempting to connect to a given endpoint
// CONNECTED - We have connected but not yet authenticated // CONNECTED - We have connected but not yet authenticated
// AUTHENTICATED - im.ricochet.auth-hidden-server has succeeded on thec onnection. // AUTHENTICATED - im.ricochet.auth-hidden-server has succeeded on the connection.
const ( const (
DISCONNECTED ConnectionState = iota DISCONNECTED ConnectionState = iota
CONNECTING CONNECTING
@ -20,4 +20,8 @@ const (
var ( var (
// ConnectionStateName allows conversion of states to their string representations // ConnectionStateName allows conversion of states to their string representations
ConnectionStateName = []string{"Disconnected", "Connecting", "Connected", "Authenticated", "Failed", "Killed"} ConnectionStateName = []string{"Disconnected", "Connecting", "Connected", "Authenticated", "Failed", "Killed"}
// ConnectionStateType allows conversion of strings to their state type
ConnectionStateType = map[string]ConnectionState{"Disconnected": DISCONNECTED, "Connecting": CONNECTING,
"Connected": CONNECTED, "Authenticated": AUTHENTICATED, "Failed": FAILED, "Killed": KILLED}
) )

View File

@ -21,6 +21,7 @@ type profileStore struct {
profile *model.Profile profile *model.Profile
eventManager *event.Manager eventManager *event.Manager
queue *event.Queue queue *event.Queue
writer bool
} }
// ProfileStore is an interface to managing the storage of Cwtch Profiles // ProfileStore is an interface to managing the storage of Cwtch Profiles
@ -30,11 +31,11 @@ type ProfileStore interface {
GetProfileCopy() *model.Profile GetProfileCopy() *model.Profile
} }
// NewProfileStore returns a profile store backed by a filestore listening for events and saving them // NewProfileWriterStore returns a profile store backed by a filestore listening for events and saving them
// directory should be $appDir/profiles/$rand // directory should be $appDir/profiles/$rand
func NewProfileStore(eventManager *event.Manager, directory, password string, profile *model.Profile) ProfileStore { func NewProfileWriterStore(eventManager *event.Manager, directory, password string, profile *model.Profile) ProfileStore {
os.Mkdir(directory, 0700) os.Mkdir(directory, 0700)
ps := &profileStore{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, eventManager: eventManager, streamStores: map[string]StreamStore{}} ps := &profileStore{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, eventManager: eventManager, streamStores: map[string]StreamStore{}, writer: true}
ps.queue = event.NewEventQueue(100) ps.queue = event.NewEventQueue(100)
if profile != nil { if profile != nil {
ps.save() ps.save()
@ -55,6 +56,15 @@ func NewProfileStore(eventManager *event.Manager, directory, password string, pr
return ps return ps
} }
// NewProfileReaderStore returns a profile store backed by a filestore
// directory should be $appDir/profiles/$rand
func NewProfileReaderStore(directory, password string, profile *model.Profile) ProfileStore {
os.Mkdir(directory, 0700)
ps := &profileStore{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, eventManager: nil, streamStores: map[string]StreamStore{}, writer: true}
return ps
}
// NewProfile creates a new profile for use in the profile store. // NewProfile creates a new profile for use in the profile store.
func NewProfile(name string) *model.Profile { func NewProfile(name string) *model.Profile {
profile := model.GenerateNewProfile(name) profile := model.GenerateNewProfile(name)
@ -62,8 +72,11 @@ func NewProfile(name string) *model.Profile {
} }
func (ps *profileStore) save() error { func (ps *profileStore) save() error {
bytes, _ := json.Marshal(ps.profile) if ps.writer {
return ps.fs.Save(bytes) bytes, _ := json.Marshal(ps.profile)
return ps.fs.Save(bytes)
}
return nil
} }
// Load instantiates a cwtchPeer from the file store // Load instantiates a cwtchPeer from the file store
@ -181,5 +194,7 @@ func (ps *profileStore) eventHandler() {
} }
func (ps *profileStore) Shutdown() { func (ps *profileStore) Shutdown() {
ps.queue.Shutdown() if ps.queue != nil {
ps.queue.Shutdown()
}
} }

View File

@ -20,7 +20,7 @@ func TestProfileStoreWriteRead(t *testing.T) {
eventBus := new(event.Manager) eventBus := new(event.Manager)
eventBus.Initialize() eventBus.Initialize()
profile := NewProfile(testProfileName) profile := NewProfile(testProfileName)
ps1 := NewProfileStore(eventBus, testingDir, password, profile) ps1 := NewProfileWriterStore(eventBus, testingDir, password, profile)
eventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{event.Key: testKey, event.Data: testVal})) eventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{event.Key: testKey, event.Data: testVal}))
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
@ -50,7 +50,7 @@ func TestProfileStoreWriteRead(t *testing.T) {
ps1.Shutdown() ps1.Shutdown()
ps2 := NewProfileStore(eventBus, testingDir, password, nil) ps2 := NewProfileWriterStore(eventBus, testingDir, password, nil)
err = ps2.Load() err = ps2.Load()
if err != nil { if err != nil {
t.Errorf("Error createing profileStore: %v\n", err) t.Errorf("Error createing profileStore: %v\n", err)

View File

@ -1,7 +1,7 @@
package testing package testing
import ( import (
"cwtch.im/cwtch/event" app2 "cwtch.im/cwtch/app"
"cwtch.im/cwtch/model" "cwtch.im/cwtch/model"
"cwtch.im/cwtch/peer" "cwtch.im/cwtch/peer"
"cwtch.im/cwtch/protocol/connections" "cwtch.im/cwtch/protocol/connections"
@ -55,16 +55,17 @@ func serverCheck(t *testing.T, serverAddr string) bool {
return true return true
} }
func waitForPeerServerConnection(t *testing.T, peer peer.CwtchPeer, server string) { func waitForPeerGroupConnection(t *testing.T, peer peer.CwtchPeer, groupID string) {
for { for {
servers := peer.GetServers() _, ok := peer.GetProfile().Groups[groupID]
state, ok := servers[server]
if ok { if ok {
state := peer.GetGroupState(groupID)
//log.Infof("Waiting for Peer %v to join group %v - state: %v\n", peer.GetProfile().Name, groupID, state)
if state == connections.FAILED { if state == connections.FAILED {
t.Fatalf("%v could not connect to %v", peer.GetProfile().Onion, server) t.Fatalf("%v could not connect to %v", peer.GetProfile().Onion, groupID)
} }
if state != connections.AUTHENTICATED { if state != connections.AUTHENTICATED {
fmt.Printf("peer %v waiting connect to server %v, currently: %v\n", peer.GetProfile().Onion, server, connections.ConnectionStateName[state]) fmt.Printf("peer %v waiting connect to group %v, currently: %v\n", peer.GetProfile().Onion, groupID, connections.ConnectionStateName[state])
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} else { } else {
@ -78,9 +79,11 @@ func waitForPeerServerConnection(t *testing.T, peer peer.CwtchPeer, server strin
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) { func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
for { for {
peers := peera.GetPeers() //peers := peera.GetPeers()
state, ok := peers[peerb.GetProfile().Onion] _, ok := peera.GetProfile().Contacts[peerb.GetProfile().Onion]
if ok { if ok {
state := peera.GetPeerState(peerb.GetProfile().Onion)
//log.Infof("Waiting for Peer %v to peer with peer: %v - state: %v\n", peera.GetProfile().Name, peerb.GetProfile().Name, state)
if state == connections.FAILED { if state == connections.FAILED {
t.Fatalf("%v could not connect to %v", peera.GetProfile().Onion, peerb.GetProfile().Onion) t.Fatalf("%v could not connect to %v", peera.GetProfile().Onion, peerb.GetProfile().Onion)
} }
@ -132,35 +135,33 @@ func TestCwtchPeerIntegration(t *testing.T) {
numGoRoutinesPostServer := runtime.NumGoroutine() numGoRoutinesPostServer := runtime.NumGoroutine()
app := app2.NewApp(acn, "./storage")
// ***** cwtchPeer setup ***** // ***** cwtchPeer setup *****
// It's important that each Peer have their own EventBus // It's important that each Peer have their own EventBus
aliceEventBus := new(event.Manager) /*aliceEventBus := new(event.Manager)
aliceEventBus.Initialize() aliceEventBus.Initialize()
bobEventBus := new(event.Manager) bobEventBus := new(event.Manager)
bobEventBus.Initialize() bobEventBus.Initialize()
carolEventBus := new(event.Manager) carolEventBus := new(event.Manager)
carolEventBus.Initialize() carolEventBus.Initialize()*/
fmt.Println("Creating Alice...") fmt.Println("Creating Alice...")
alice := peer.NewCwtchPeer("Alice") alice, _ := app.CreatePeer("alice", "asdfasdf")
alice.Init(acn, aliceEventBus)
alice.Listen()
fmt.Println("Alice created:", alice.GetProfile().Onion) fmt.Println("Alice created:", alice.GetProfile().Onion)
fmt.Println("Creating Bob...") fmt.Println("Creating Bob...")
bob := peer.NewCwtchPeer("Bob") bob, _ := app.CreatePeer("bob", "asdfasdf")
bob.Init(acn, bobEventBus)
bob.Listen()
fmt.Println("Bob created:", bob.GetProfile().Onion) fmt.Println("Bob created:", bob.GetProfile().Onion)
fmt.Println("Creating Carol...") fmt.Println("Creating Carol...")
carol := peer.NewCwtchPeer("Carol") carol, _ := app.CreatePeer("Carol", "asdfasdf")
carol.Init(acn, carolEventBus)
carol.Listen()
fmt.Println("Carol created:", carol.GetProfile().Onion) fmt.Println("Carol created:", carol.GetProfile().Onion)
fmt.Println("Waiting for Alice, Bob, and Carol to connection with onion network...") app.LaunchPeers()
fmt.Println("Waiting for Alice, Bob, and Carol to connect with onion network...")
time.Sleep(time.Second * 90) time.Sleep(time.Second * 90)
numGoRoutinesPostPeerStart := runtime.NumGoroutine() numGoRoutinesPostPeerStart := runtime.NumGoroutine()
@ -175,8 +176,10 @@ func TestCwtchPeerIntegration(t *testing.T) {
} }
fmt.Println("Alice peering with Bob...") fmt.Println("Alice peering with Bob...")
alice.AddContact("Bob", bob.GetProfile().Onion, false) // Add contact so we can track connection state
alice.PeerWithOnion(bob.GetProfile().Onion) alice.PeerWithOnion(bob.GetProfile().Onion)
fmt.Println("Alice peering with Carol...") fmt.Println("Alice peering with Carol...")
alice.AddContact("Carol", carol.GetProfile().Onion, false)
alice.PeerWithOnion(carol.GetProfile().Onion) alice.PeerWithOnion(carol.GetProfile().Onion)
fmt.Println("Alice joining server...") fmt.Println("Alice joining server...")
@ -185,10 +188,10 @@ func TestCwtchPeerIntegration(t *testing.T) {
bob.JoinServer(serverAddr) bob.JoinServer(serverAddr)
fmt.Println("Waiting for alice to join server...") fmt.Println("Waiting for alice to join server...")
waitForPeerServerConnection(t, alice, serverAddr) waitForPeerGroupConnection(t, alice, groupID)
fmt.Println("Waiting for bob to join server...") //fmt.Println("Waiting for bob to join server...")
waitForPeerServerConnection(t, bob, serverAddr) //waitForPeerGroupConnection(t, bob, groupID)
fmt.Println("Waiting for alice and Bob to peer...") fmt.Println("Waiting for alice and Bob to peer...")
waitForPeerPeerConnection(t, alice, bob) waitForPeerPeerConnection(t, alice, bob)
@ -217,35 +220,9 @@ func TestCwtchPeerIntegration(t *testing.T) {
numGoRoutinesPostServerConnect := runtime.NumGoroutine() numGoRoutinesPostServerConnect := runtime.NumGoroutine()
// ***** Fill up message history of server ******
/*
// filler group will be used to fill up the servers message history a bit to stress test fetch later for carol
fillerGroupId, _, err := alice.Profile.StartGroup(serverAddr)
if err != nil {
t.Errorf("Failed to init filler group: %v", err)
return
}
fmt.Println("Alice filling message history of server...")
for i := 0; i < 100; i++ {
go func (x int) {
time.Sleep(time.Second * time.Duration(x))
err := alice.SendMessageToGroup(fillerGroupId, aliceLines[0])
if err != nil {
fmt.Println("SEND", x, "ERROR:", err)
} else {
fmt.Println("SEND", x, " SUCCESS!")
}
}(i)
}
time.Sleep(time.Second * 110)
*/
// Wait for them to join the server // Wait for them to join the server
waitForPeerServerConnection(t, alice, serverAddr) waitForPeerGroupConnection(t, alice, groupID)
waitForPeerServerConnection(t, bob, serverAddr) waitForPeerGroupConnection(t, bob, groupID)
//numGouRoutinesPostServerConnect := runtime.NumGoroutine() //numGouRoutinesPostServerConnect := runtime.NumGoroutine()
// ***** Conversation ***** // ***** Conversation *****
@ -291,14 +268,13 @@ func TestCwtchPeerIntegration(t *testing.T) {
} }
fmt.Println("Shutting down Alice...") fmt.Println("Shutting down Alice...")
alice.Shutdown() app.ShutdownPeer(alice.GetProfile().Onion)
aliceEventBus.Shutdown()
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
numGoRoutinesPostAlice := runtime.NumGoroutine() numGoRoutinesPostAlice := runtime.NumGoroutine()
fmt.Println("Carol joining server...") fmt.Println("Carol joining server...")
carol.JoinServer(serverAddr) carol.JoinServer(serverAddr)
waitForPeerServerConnection(t, carol, serverAddr) waitForPeerGroupConnection(t, carol, groupID)
numGoRotinesPostCarolConnect := runtime.NumGoroutine() numGoRotinesPostCarolConnect := runtime.NumGoroutine()
fmt.Println("Bob> ", bobLines[2]) fmt.Println("Bob> ", bobLines[2])
@ -380,8 +356,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
} }
fmt.Println("Shutting down Bob...") fmt.Println("Shutting down Bob...")
bob.Shutdown() app.ShutdownPeer(bob.GetProfile().Onion)
bobEventBus.Shutdown()
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
numGoRoutinesPostBob := runtime.NumGoroutine() numGoRoutinesPostBob := runtime.NumGoroutine()
if server != nil { if server != nil {
@ -392,8 +367,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
numGoRoutinesPostServerShutdown := runtime.NumGoroutine() numGoRoutinesPostServerShutdown := runtime.NumGoroutine()
fmt.Println("Shutting down Carol...") fmt.Println("Shutting down Carol...")
carol.Shutdown() app.ShutdownPeer(carol.GetProfile().Onion)
carolEventBus.Shutdown()
time.Sleep(time.Second * 3) time.Sleep(time.Second * 3)
numGoRoutinesPostCarol := runtime.NumGoroutine() numGoRoutinesPostCarol := runtime.NumGoroutine()