From 850ac6914d8a40713158fd685a026d83c3285405 Mon Sep 17 00:00:00 2001 From: Lars Bahner Date: Mon, 13 Nov 2023 06:10:49 +0100 Subject: [PATCH] Refactor to match go-ma. --- .gitignore | 1 + README.md | 41 +++++--------- TODO | 5 +- actor.go | 73 ------------------------- actor/actor.go | 104 +++++++++++++++++++++++++++++++++++ actor/enter.go | 20 +++++++ actor/listen.go | 32 +++++++++++ actor/private.go | 44 +++++++++++++++ actor/public.go | 49 +++++++++++++++++ addrs.go | 15 ++++++ config.go | 74 ------------------------- config/config.go | 138 +++++++++++++++++++++++++++++++++++++++++++++++ config/keyset.go | 60 +++++++++++++++++++++ go.mod | 12 ++--- go.sum | 41 +++++--------- keyset.go | 67 ----------------------- main.go | 65 +++++++++++++--------- message.go | 38 ------------- p2p.go | 37 ------------- room.go | 132 --------------------------------------------- room/room.go | 17 ++++++ ui.go | 61 +++++++++++---------- 22 files changed, 585 insertions(+), 541 deletions(-) delete mode 100644 actor.go create mode 100644 actor/actor.go create mode 100644 actor/enter.go create mode 100644 actor/listen.go create mode 100644 actor/private.go create mode 100644 actor/public.go create mode 100644 addrs.go delete mode 100644 config.go create mode 100644 config/config.go create mode 100644 config/keyset.go delete mode 100644 keyset.go delete mode 100644 message.go delete mode 100644 p2p.go delete mode 100644 room.go create mode 100644 room/room.go diff --git a/.gitignore b/.gitignore index e45bfd2..8d1cc23 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ go-space-thumb* .env *secret go-ma-actor +go-home diff --git a/README.md b/README.md index e707ca7..dc21bbc 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,30 @@ -# github.com/bahner/go-space-thumb +# github.com/bahner/go-home -This is go-space-thumb based on [an example from go-libp2p][src]. +This is go-home based on [an example from go-libp2p][src]. Now you can either run with `go run`, or build and run the binary: ```shell -go run . -identity foobar -room myTopic - -# or, build and run separately go build . - export GO_MA_ACTOR_IDENTITY=fooBar -./go-space-thumb -room myTopic +./go-home -genenv -forcPublish > .env // Generate persistent environment variables of *SECRET* keysets +. .env // Load the environment variables +./go-home // Run the app ``` ## Configuration -type `./go-space-thumb -help`. Most config settings can be set with environment variables, as follows: +type `./go-home -help`. Most config settings can be set with environment variables, as follows: ```bash -export GO_MA_ACTOR_LOG_LEVEL="error" -export GO_MA_ACTOR_RENDEZVOUS="space" -export GO_MA_ACTOR_SERVICE_NAME="space" -export GO_MA_ACTOR_ROOM="mytopic" -export GO_MA_ACTOR_IDENTITY="myBase58EncodedPrivkeyGeneratedByGenerate" +export GO_HOME_LOG_LEVEL="error" +export GO_HOME_DISCOVERY_TIMEOUT="300" +export GO_HOME_ACTOR_KEYSET="myBase58EncodedPrivkeyGeneratedByGenerate" +export GO_HOME_ROOM_KEYSET="myBase58EncodedPrivkeyGeneratedByGenerate" ``` ## Identity -A `-generate` parameter to generate a text version of a secret key. +A `-generate` or `genenv` parameter to generate a text version of a secret key. The key is text formatted privKey for your node. This key can and should be kept safely on a PostIt note on your monitor :-) @@ -35,30 +32,20 @@ Just don't store somewhere insecure. It's your future identity. ```bash unset HISTFILE - export GO_MA_ACTOR_IDENTITY=FooBarABCDEFbase58 + export GO_HOME_ACTOR_KEYSET=FooBarABCDEFbase58 + export GO_HOME_ROOM_KEYSET=FooBarABCDEFGHIbase58 ``` or specified on the command line: ```bash -./go-space-thumb -identity FooBarABCDEFbase58 +./go-home -actorKeyset FooBarABCDEFbase58 -roomKeyset FooBarABCDEFGHIbase58 ``` The first is the best. (Noticed that in most shells the empty space before the command, means that the line isn't saved in history.) ## Usage -You can join a specific chat room with the `-room` flag: - -```shell -go run . -room=planet-express -``` - -It's usually more fun to chat with others, so open a new terminal and run the app again. -If you set a custom chat room name with the `-room` flag, make sure you use the same one -for both apps. Once the new instance starts, the two chat apps should discover each other -automatically using mDNS, and typing a message into one app will send it to any others that are open. - To quit, hit `Ctrl-C`, or type `/quit` into the input field. ## Commands diff --git a/TODO b/TODO index d3d996a..afb7cdb 100644 --- a/TODO +++ b/TODO @@ -3,9 +3,6 @@ * redirect logs from stdout to file ## Features -* integrate ipld - - store message as ipld - - sign dagcid * integrate ipns - publish did with key - + - There is some logical error wityh handling existing keys diff --git a/actor.go b/actor.go deleted file mode 100644 index bddb2e7..0000000 --- a/actor.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/bahner/go-ma/did" - "github.com/bahner/go-ma/did/doc" - "github.com/bahner/go-ma/key/set" - "github.com/bahner/go-ma/message" - pubsub "github.com/libp2p/go-libp2p-pubsub" - - log "github.com/sirupsen/logrus" -) - -type Actor struct { - Keyset *set.Keyset - DID *did.DID - Doc *doc.Document - - // PubSub Attributes - From *pubsub.Subscription // The subscription to the topic for receiving messages. - - // By using antropomorphic terms, we underline that this is a special use case. - // mouth chan *message.Message // Messages to send - ears chan *message.Message // Received messages - // hands chan string // Local command input. Simple commands like /quit, /help etc. -} - -func initActor(k *set.Keyset) (*Actor, error) { - - a := &Actor{} - var err error - - log.Debugf("Setting Actor Keyset: %v", k) - a.Keyset = k - - // Add the DID fragment as a field to the actor. - a.DID, err = did.NewFromIPNSKey(a.Keyset.IPNSKey) - if err != nil { - return nil, fmt.Errorf("new_actor: Failed to create DID: %v", err) - } - log.Debugf("new_actor: Created DID: %s", a.DID.String()) - - // Publish the IPNSKey to IPFS for publication. - err = k.IPNSKey.ExportToIPFS(a.DID.Fragment, *forcePublish) - if err != nil { - return nil, fmt.Errorf("new_actor: Failed to export IPNSKey to IPFS: %v", err) - } - - // Make sure the actor has a DOC and published DIDDocument. - a.Doc, err = doc.NewFromKeyset(a.Keyset, a.DID.String()) - if err != nil { - return nil, fmt.Errorf("new_actor: Failed to create DOC: %v", err) - } - - _, err = a.Doc.Publish() - if err != nil { - return nil, fmt.Errorf("new_actor: Failed to publish DOC: %v", err) - } - - // We can now - recvTopic, err := ps.Sub.Join(node.Node.ID().String()) // The ipnskey is the id of the actor. - if err != nil { - return nil, fmt.Errorf("new_actor: Failed to join topic: %v", err) - } - - a.From, err = recvTopic.Subscribe() - if err != nil { - return nil, fmt.Errorf("new_actor: Failed to subscribe to topic: %v", err) - } - - return a, nil -} diff --git a/actor/actor.go b/actor/actor.go new file mode 100644 index 0000000..38bd925 --- /dev/null +++ b/actor/actor.go @@ -0,0 +1,104 @@ +package actor + +import ( + "context" + "fmt" + + "github.com/bahner/go-ma/entity" + "github.com/bahner/go-ma/key/set" + "github.com/bahner/go-ma/msg" + pubsub "github.com/libp2p/go-libp2p-pubsub" + + log "github.com/sirupsen/logrus" +) + +const MESSAGES_BUFFERSIZE = 100 + +type Actor struct { + + // This context is used to cancel the Listen() function. + ctx context.Context + + // ps service ointer + ps *pubsub.PubSub + + // All actors must be entities. + // Ideally they should be the same, but then ma becomes a bit too opinionated. + Entity *entity.Entity + + // Private is the topic where we receive envelopes from other actors. + // It's basically a private channel with the DIDDocument keyAgreement as topic. + Private *pubsub.Topic + + // We basically receive signed messages from the room we're in here. + // It's basically a public channel with the assertionMethod from the DIDDocument of + // the room we're in as topic. + // Others can subscribe to this topic and send us messages, as long as they are signed. + Public *pubsub.Topic + + // Incoming messages from the actor to AssertionMethod topic. It's bascially a broadcast channel. + // But you could use it to send messages to a specific actor or to all actors in a group. + // This is a public channel. There will need to be some generic To (recipients) in the mesage + // for example "broadcast", so that one actor can send a message to everybody in the room. + // That is a TODO. + // We receive the message contents here after verification or decryption. + Messages chan *msg.Message +} + +// Creates a new actor from an entity. +// Takes a pubsub.PubSub service, an entity and a forcePublish flag. +// The forcePublish is to override existing keys in IPFS. +func New(ctx context.Context, ps *pubsub.PubSub, e *entity.Entity, forcePublish bool) (*Actor, error) { + + log.Debugf("actor/new: Setting Actor Entity: %v", e) + + var err error + a := &Actor{} + + // Assign provided resource pointers + a.ctx = ctx + a.ps = ps + + // Firstly create assign entity to actor + a.Entity = e + + // Create topic for incoming envelopes + a.Private, err = ps.Join(a.Entity.Doc.KeyAgreement) + if err != nil { + if err.Error() != "topic already exists" { + return nil, fmt.Errorf("new_actor: Failed to join topic: %v", err) + } + } + + // Create subscription to topic for incoming messages + a.Public, err = ps.Join(a.Entity.Doc.AssertionMethod) + if err != nil { + return nil, fmt.Errorf("new_actor: Failed to join topic: %v", err) + } + + // Set the messages channel + a.Messages = make(chan *msg.Message, MESSAGES_BUFFERSIZE) + + // Publish the entity + err = a.Entity.Publish(forcePublish) + if err != nil { + return nil, fmt.Errorf("new_actor: Failed to publish Entity: %v", err) + } + + log.Debugf("new_actor: Actor initialized: %s", a.Entity.DID.Fragment) + return a, nil + +} + +// Creates a new actor from a keyset. +// Takes a pubsub.PubSub service, a keyset and a forcePublish flag. +func NewFromKeyset(ctx context.Context, ps *pubsub.PubSub, k *set.Keyset, forcePublish bool) (*Actor, error) { + + log.Debugf("Setting Actor Entity: %v", k) + e, err := entity.NewFromKeyset(k) + if err != nil { + return nil, fmt.Errorf("new_actor: Failed to create Entity: %v", err) + } + + return New(ctx, ps, e, forcePublish) +} diff --git a/actor/enter.go b/actor/enter.go new file mode 100644 index 0000000..27777eb --- /dev/null +++ b/actor/enter.go @@ -0,0 +1,20 @@ +package actor + +import "fmt" + +// Takes a room topic and joins it. The room is the DID of the room actor. +func (a *Actor) Enter(room string) error { + + var err error + + // First close the current subscription + a.Public.Close() + + a.Public, err = a.ps.Join(room) + if err != nil { + return fmt.Errorf("home: %v failed to join topic: %v", a, err) + } + + return nil + +} diff --git a/actor/listen.go b/actor/listen.go new file mode 100644 index 0000000..48ba82e --- /dev/null +++ b/actor/listen.go @@ -0,0 +1,32 @@ +package actor + +import ( + "fmt" +) + +func (a *Actor) Listen(outputChannel chan<- string) error { + // Subscribe to Inbox topic + inboxSub, err := a.Private.Subscribe() + if err != nil { + return fmt.Errorf("failed to subscribe to Inbox topic: %v", err) + } + defer inboxSub.Cancel() + + // Subscribe to Space topic + spaceSub, err := a.Public.Subscribe() + if err != nil { + return fmt.Errorf("failed to subscribe to Space topic: %v", err) + } + defer spaceSub.Cancel() + + // Start a goroutine for Inbox subscription + go a.handlePrivateMessages(inboxSub) + + // Start a goroutine for Space subscription + // Assuming you have a similar function for Space + go a.handlePublicMessages(spaceSub) + + // Wait for context cancellation (or other exit conditions) + <-a.ctx.Done() + return a.ctx.Err() +} diff --git a/actor/private.go b/actor/private.go new file mode 100644 index 0000000..3fd9f64 --- /dev/null +++ b/actor/private.go @@ -0,0 +1,44 @@ +package actor + +import ( + "fmt" + + "github.com/bahner/go-ma/msg" + "github.com/bahner/go-ma/msg/envelope" + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +func (a *Actor) receivePrivateEnvelopes(sub *pubsub.Subscription) (*msg.Message, error) { + + msgData, err := sub.Next(a.ctx) + if err != nil { + return nil, fmt.Errorf("failed to receive message from inbox: %v", err) + } + + e, err := envelope.UnmarshalFromCBOR(msgData.Data) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal envelope from CBOR: %v", err) + } + + message, err := e.Open(a.Entity.Keyset.EncryptionKey.PrivKey) + if err != nil { + return nil, fmt.Errorf("failed to open envelope: %v", err) + } + + return message, nil +} + +func (a *Actor) handlePrivateMessages(sub *pubsub.Subscription) { + for { + select { + case <-a.ctx.Done(): + // Exit goroutine when context is cancelled + return + default: + // Read message from Inbox subscription + if msg, err := a.receivePrivateEnvelopes(sub); err == nil { + a.Messages <- msg + } + } + } +} diff --git a/actor/public.go b/actor/public.go new file mode 100644 index 0000000..0c4a99e --- /dev/null +++ b/actor/public.go @@ -0,0 +1,49 @@ +package actor + +import ( + "fmt" + + "github.com/bahner/go-ma/msg" + pubsub "github.com/libp2p/go-libp2p-pubsub" +) + +func (a *Actor) receivePublicMessages(sub *pubsub.Subscription) (*msg.Message, error) { + + msgData, err := sub.Next(a.ctx) + if err != nil { + return nil, fmt.Errorf("failed to receive message from inbox: %v", err) + } + + m, err := msg.Unpack(string(msgData.Data)) + if err != nil { + return nil, fmt.Errorf("failed to unpack message: %v", err) + } + + // Quickly check if message is signed before we try to verify it. + // This is where DOS'ing might happen, so... + if m.Signature == "" { + return nil, fmt.Errorf("message has no signature") + } + + _, err = m.Verify() + if err != nil { + return nil, fmt.Errorf("failed to verify message: %v", err) + } + + return m, nil +} + +func (a *Actor) handlePublicMessages(sub *pubsub.Subscription) { + for { + select { + case <-a.ctx.Done(): + // Exit goroutine when context is cancelled + return + default: + // Read message from Inbox subscription + if msg, err := a.receivePublicMessages(sub); err == nil { + a.Messages <- msg + } + } + } +} diff --git a/addrs.go b/addrs.go new file mode 100644 index 0000000..d08cead --- /dev/null +++ b/addrs.go @@ -0,0 +1,15 @@ +package main + +func getListenAddrStrings(port string) []string { + + return []string{ + "/ip4/0.0.0.0/tcp/" + port, + "/ip4/0.0.0.0/udp/" + port + "/quic", + "/ip4/0.0.0.0/udp/" + port + "/quic-v1", + "/ip4/0.0.0.0/udp/" + port + "/quic-v1/webtransport", + "/ip6/::/tcp/" + port, + "/ip6/::/udp/" + port + "/quic", + "/ip6/::/udp/" + port + "/quic-v1", + "/ip6/::/udp/" + port + "/quic-v1/webtransport", + } +} diff --git a/config.go b/config.go deleted file mode 100644 index 1dec234..0000000 --- a/config.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "flag" - - "github.com/bahner/go-ma/key/set" - "github.com/bahner/go-space/p2p/host" - "github.com/bahner/go-space/p2p/pubsub" - log "github.com/sirupsen/logrus" - "go.deanishe.net/env" -) - -var ( - logLevel string = env.Get("GO_MA_ACTOR_LOG_LEVEL", "error") - rendezvous string = env.Get("GO_MA_ACTOR_RENDEZVOUS", "/ma/0.0.1") - serviceName string = env.Get("GO_MA_ACTOR_SERVICE_NAME", "/ma/0.0.1") - nick string = env.Get("USER", "ghost") - room string = env.Get("GO_MA_ACTOR_ROOM", "mytopic") - keyset string = env.Get("GO_MA_ACTOR_KEYSET", "") - - generate *bool - genenv *bool - publish *bool - forcePublish *bool - - identity *set.Keyset - node *host.Host - ps *pubsub.Service -) - -func initConfig() { - - // Flags - user configurations - flag.StringVar(&logLevel, "loglevel", logLevel, "Loglevel to use for application") - flag.StringVar(&rendezvous, "rendezvous", rendezvous, "Unique string to identify group of nodes. Share this with your friends to let them connect with you") - flag.StringVar(&serviceName, "servicename", serviceName, "serviceName to use for MDNS discovery") - flag.StringVar(&room, "room", room, "Room (topic) to join. This is obviously a TODO as we need more.") - flag.StringVar(&nick, "nick", nick, "Nickname to use in character creation") - - // The secret sauce. Use or generate a new one. - flag.StringVar(&keyset, "keyset", keyset, "Base58 encoded secret key used to identify the client. You.") - - generate = flag.Bool("generate", false, "Generate a new private key, prints it and exit the program.") - genenv = flag.Bool("genenv", false, "Generates a new environment file with a new private key to stdout") - publish = flag.Bool("publish", false, "Publishes keyset to IPFS when using genenv or generate") - forcePublish = flag.Bool("force-publish", false, "Force publish even if keyset is already published") - - flag.Parse() - - // Init logger - level, err := log.ParseLevel(logLevel) - if err != nil { - log.Fatal(err) - } - log.SetLevel(level) - log.Info("Logger initialized") - - // Generate a new keyset if requested - if *generate || *genenv { - generateKeyset(nick) - } - - // Assign the identity - if keyset == "" { - log.Fatal("You need to set a secret key unless you generate a new one.") - } - - unpackedKeyset, err := set.Unpack(keyset) - if err != nil { - log.Fatalf("Failed to unpack keyset: %v", err) - } - identity = &unpackedKeyset - log.Debug("Unpacked keyset and set it to actor.") -} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..1770076 --- /dev/null +++ b/config/config.go @@ -0,0 +1,138 @@ +package config + +import ( + "flag" + "os" + "time" + + "github.com/bahner/go-ma/key/set" + nanoid "github.com/matoous/go-nanoid/v2" + log "github.com/sirupsen/logrus" + "go.deanishe.net/env" +) + +const ( + actor_keyset_var = "GO_HOME_ACTOR_KEYSET" + room_keyset_var = "GO_HOME_ROOM_KEYSET" + defaultDiscoveryTimeout = 300 +) + +var ( + discoveryTimeout int = env.GetInt("GO_HOME_DISCOVERY_TIMEOUT", defaultDiscoveryTimeout) + + logLevel string = env.Get("GO_HOME_LOG_LEVEL", "error") + nick string = env.Get("USER") + actor_keyset_string string = env.Get(actor_keyset_var, "") + room_keyset_string string = env.Get(room_keyset_var, "") + + generate *bool + genenv *bool + publish *bool + forcePublish *bool + + RoomKeyset *set.Keyset + ActorKeyset *set.Keyset + + randomRoomNick, _ = nanoid.New() +) + +func Init() { + + // Flags - user configurations + flag.StringVar(&logLevel, "loglevel", logLevel, "Loglevel to use for application") + flag.IntVar(&discoveryTimeout, "discoveryTimeout", discoveryTimeout, "Timeout for peer discovery") + + // Actor + flag.StringVar(&nick, "nick", nick, "Nickname to use in character creation") + flag.StringVar(&actor_keyset_string, "actorKeyset", actor_keyset_string, "Base58 encoded secret key used to identify the client. You.") + + // Room + flag.StringVar(&room_keyset_string, "roomKeyset", room_keyset_string, "Base58 encoded secret key used to identify your room.") + + // Booleans with control flow + generate = flag.Bool("generate", false, "Generates one-time keyset and uses it") + genenv = flag.Bool("genenv", false, "Generates a keyset and prints it to stdout and uses it") + publish = flag.Bool("publish", false, "Publishes keyset to IPFS when using genenv or generate") + forcePublish = flag.Bool("forcePublish", false, "Force publish even if keyset is already published") + + flag.Parse() + + // Init logger + level, err := log.ParseLevel(logLevel) + if err != nil { + log.Fatal(err) + } + log.SetLevel(level) + log.Info("Logger initialized") + + // Generate a new keysets if requested + if *generate || *genenv { + actor_keyset_string = generateKeyset(actor_keyset_var, nick, *forcePublish) + room_keyset_string = generateKeyset(room_keyset_var, randomRoomNick, *forcePublish) + } + + if *publish || *forcePublish { + if actor_keyset_string != "" { + publishKeyset(ActorKeyset, *forcePublish) + } else { + log.Errorf("No actor keyset to publish.") + } + + if room_keyset_string != "" { + publishKeyset(RoomKeyset, *forcePublish) + } else { + log.Errorf("No room keyset to publish.") + } + } + + if *genenv || *generate { + os.Exit(0) + } + + log.Debugf("actor_keyset_string: %s", actor_keyset_string) + // Create the actor keyset + if actor_keyset_string == "" { + log.Fatal("You need to define actorKeyset or generate a new one.") + } + unpackedActorKeyset, err := set.Unpack(actor_keyset_string) + if err != nil { + log.Fatalf("Failed to unpack keyset: %v", err) + } + ActorKeyset = unpackedActorKeyset + + // Create the room keyset + if room_keyset_string == "" { + log.Fatal("You need to define roomKeyset or generate a new one.") + } + unpackedRoomKeyset, err := set.Unpack(room_keyset_string) + if err != nil { + log.Fatalf("Failed to unpack keyset: %v", err) + } + RoomKeyset = unpackedRoomKeyset + + log.Debug("Unpacked keyset and set it to actor.") +} + +func GetActorKeyset() *set.Keyset { + return ActorKeyset +} + +func GetRoomKeyset() *set.Keyset { + return RoomKeyset +} + +func GetNick() string { + return nick +} + +func GetLogLevel() string { + return logLevel +} + +func GetForcePublish() bool { + return *forcePublish +} + +func GetDiscoveryTimeout() time.Duration { + return time.Duration(discoveryTimeout) * time.Second +} diff --git a/config/keyset.go b/config/keyset.go new file mode 100644 index 0000000..64902c2 --- /dev/null +++ b/config/keyset.go @@ -0,0 +1,60 @@ +package config + +import ( + "fmt" + + "github.com/bahner/go-ma/did/doc" + "github.com/bahner/go-ma/key/set" + log "github.com/sirupsen/logrus" +) + +func generateKeyset(variableName string, name string, forceUpdate bool) string { + + if nick == "ghost" { + log.Fatal("You need to set a nick when generating an identity.") + } + + ks, err := set.New(name, forceUpdate) + if err != nil { + log.Fatalf("Failed to generate new keyset: %v", err) + } + + pks, err := ks.Pack() + if err != nil { + log.Fatalf("Failed to pack keyset: %v", err) + } + + if *genenv { + fmt.Println("export " + variableName + "=" + pks) + } + + return pks +} + +func publishKeyset(ks *set.Keyset, forcePublish bool) { + + log.Debugf("generate_keyset: Publishing secret IPNSKey to IPFS: %v", ks.IPNSKey.PublicKey) + err := ks.IPNSKey.ExportToIPFS(forcePublish) + if err != nil { + log.Warnf("create_and_print_keyset: failed to export keyset: %v", err) + } + log.Infof("create_and_print_keyset: exported IPNSkey to IPFS: %s", ks.IPNSKey.DID) + + d, err := doc.NewFromKeyset(ks, ks.IPNSKey.DID) + if err != nil { + log.Fatalf("create_and_print_keyset: failed to create DOC: %v", err) + } + + assertionMethod, err := d.GetAssertionMethod() + if err != nil { + log.Fatalf("create_and_print_keyset: failed to get verification method: %v", err) + } + d.Sign(ks.SigningKey, assertionMethod) + + _, err = d.Publish() + if err != nil { + log.Fatalf("create_and_print_keyset: failed to publish DOC: %v", err) + } + log.Debugf("create_and_print_keyset: published DOC: %s", d.ID) + +} diff --git a/go.mod b/go.mod index 9048ae2..9ebf084 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ -module github.com/bahner/go-ma-actor +module github.com/bahner/go-home -go 1.21.4 +go 1.21 require ( - github.com/bahner/go-ma v0.0.0-20231113043616-c67b8430a629 - github.com/bahner/go-space v0.0.0-20231030191946-a78651000d09 + github.com/bahner/go-ma v0.0.3-0.20231120152939-51cf67625bba github.com/gdamore/tcell/v2 v2.6.0 github.com/libp2p/go-libp2p v0.32.1 github.com/libp2p/go-libp2p-pubsub v0.10.0 - github.com/rivo/tview v0.0.0-20231102183219-1b91b8131c43 + github.com/matoous/go-nanoid/v2 v2.0.0 + github.com/rivo/tview v0.0.0-20231115183240-7c9e464bac02 github.com/sirupsen/logrus v1.9.3 go.deanishe.net/env v0.5.1 ) @@ -70,10 +70,8 @@ require ( github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect - github.com/libp2p/zeroconf/v2 v2.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/matoous/go-nanoid/v2 v2.0.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect diff --git a/go.sum b/go.sum index f431480..9b5e163 100644 --- a/go.sum +++ b/go.sum @@ -9,24 +9,18 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/bahner/go-ma v0.0.0-20231113013031-f885d61e9a08 h1:0ZEuQ0YsguP/vsq+4VbLFTKFUSN1Ohf0Pezz6kxzjpI= -github.com/bahner/go-ma v0.0.0-20231113013031-f885d61e9a08/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113030924-775372c44749 h1:SUnGUSdAE/gfxpLMMS51hUK3+FbOyFR0ZNJSpvJ/ncg= -github.com/bahner/go-ma v0.0.0-20231113030924-775372c44749/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113033323-aba12180766d h1:D9j4lk/AKGbkMoxruos9AXb6qtfF69zXc4Tm88bLYL8= -github.com/bahner/go-ma v0.0.0-20231113033323-aba12180766d/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113034039-d29f43876e39 h1:i0LLIWmF0vPHuIGemQXiuQcCdWQqgUHyd9lTqQXeYGw= -github.com/bahner/go-ma v0.0.0-20231113034039-d29f43876e39/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113035220-37dec743fc0b h1:95sbBORXiwr4o5WKhMlc8sPHyY/lnI3eijMHQ+pc8as= -github.com/bahner/go-ma v0.0.0-20231113035220-37dec743fc0b/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113040448-0ae8b54d5862 h1:sw/9OdKSqHls6sN+IKwq9Y4RpZ/cqZHxgdCO8F/pYn4= -github.com/bahner/go-ma v0.0.0-20231113040448-0ae8b54d5862/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113041158-a97077100f81 h1:aNh9OjmvKj0iESxeR+JebteQ82faNCCZxekOyAHwwp4= -github.com/bahner/go-ma v0.0.0-20231113041158-a97077100f81/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-ma v0.0.0-20231113043616-c67b8430a629 h1:h3lsKvFBIsg0ErwL/M0lWl+ZIZyN1NvczNm5Fths0yw= -github.com/bahner/go-ma v0.0.0-20231113043616-c67b8430a629/go.mod h1:x5WBj/O1ZSJs8oxrVKZ1BTmJ1x3bNL1+qhvlX6jeq/E= -github.com/bahner/go-space v0.0.0-20231030191946-a78651000d09 h1:rVi4FyvJ0dxC3ya+v44DaTMubxfbFGfrFgTsirO8s5E= -github.com/bahner/go-space v0.0.0-20231030191946-a78651000d09/go.mod h1:cgu8XLCeBeRn2Adggrr8XVmiX35odqexQkKGkcSHWGA= +github.com/bahner/go-ma v0.0.3-0.20231120010614-efc72eb6c268 h1:cb2UwdG572gR9CcaBMDPRRw7QD6PNb/ODioKH1uaJh4= +github.com/bahner/go-ma v0.0.3-0.20231120010614-efc72eb6c268/go.mod h1:MWZULjMCo2lxiCffsp+V7H5TVk/5jfIzqu14a0lyCSs= +github.com/bahner/go-ma v0.0.3-0.20231120012422-08fb805f233d h1:N0efS9wA62I//0n3hxqVaQH4kXTVNRb/JSFRIlx0CpE= +github.com/bahner/go-ma v0.0.3-0.20231120012422-08fb805f233d/go.mod h1:MWZULjMCo2lxiCffsp+V7H5TVk/5jfIzqu14a0lyCSs= +github.com/bahner/go-ma v0.0.3-0.20231120013446-5c8c42751939 h1:UdVuu3m/czpEL5B3KV2JhrDfvqVbi2hdwURBn7OfYgY= +github.com/bahner/go-ma v0.0.3-0.20231120013446-5c8c42751939/go.mod h1:MWZULjMCo2lxiCffsp+V7H5TVk/5jfIzqu14a0lyCSs= +github.com/bahner/go-ma v0.0.3-0.20231120013716-db9426979605 h1:HPRyhvzMLpsBpnMmrGdCy5GRZbaIqnDHILLPHShlyyM= +github.com/bahner/go-ma v0.0.3-0.20231120013716-db9426979605/go.mod h1:MWZULjMCo2lxiCffsp+V7H5TVk/5jfIzqu14a0lyCSs= +github.com/bahner/go-ma v0.0.3-0.20231120014007-84ec4090ac12 h1:fqC2j/cN9ob5NAbihmUof0n/jTAbukiBzK+nIErxgbo= +github.com/bahner/go-ma v0.0.3-0.20231120014007-84ec4090ac12/go.mod h1:MWZULjMCo2lxiCffsp+V7H5TVk/5jfIzqu14a0lyCSs= +github.com/bahner/go-ma v0.0.3-0.20231120152939-51cf67625bba h1:ry+7oey2YURSlCOMO8beDhwaHAQXLfsMqVsRGO+210k= +github.com/bahner/go-ma v0.0.3-0.20231120152939-51cf67625bba/go.mod h1:MWZULjMCo2lxiCffsp+V7H5TVk/5jfIzqu14a0lyCSs= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -254,8 +248,6 @@ github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQsc github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= -github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -275,7 +267,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= @@ -363,8 +354,8 @@ github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFD github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/rivo/tview v0.0.0-20231102183219-1b91b8131c43 h1:2b19kXs3HdZLq3yRRFnEGIbLrbh5FdewdpcJJFHebg4= -github.com/rivo/tview v0.0.0-20231102183219-1b91b8131c43/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE= +github.com/rivo/tview v0.0.0-20231115183240-7c9e464bac02 h1:UkSrnoeeuKdeNFe4ghSjZmp7tA5B1CQKnvV1By9FSYw= +github.com/rivo/tview v0.0.0-20231115183240-7c9e464bac02/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -517,7 +508,6 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -551,8 +541,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -571,7 +559,6 @@ golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= diff --git a/keyset.go b/keyset.go deleted file mode 100644 index 32fd5b4..0000000 --- a/keyset.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/bahner/go-ma/did/doc" - "github.com/bahner/go-ma/key/set" - log "github.com/sirupsen/logrus" -) - -func generateKeyset(name string) { - - if keyset != "" { - log.Info("Ignoring keyset flag as generate is set.") - } - - if nick == "ghost" { - log.Fatal("You need to set a nick when generating an identity.") - } - - ks, err := set.New(name) - if err != nil { - log.Fatalf("Failed to generate new keyset: %v", err) - } - - pks, err := ks.Pack() - if err != nil { - log.Fatalf("Failed to pack keyset: %v", err) - } - - if *genenv { - fmt.Println("export GO_MA_ACTOR_KEYSET=" + pks) - } else { - fmt.Println(pks) - } - - log.Debugf("generate_keyset: Generated new keyset: %v", ks) - - if *publish || *forcePublish { - log.Debugf("generate_keyset: Publishing secret IPNSKey to IPFS: %v", ks.IPNSKey.PublicKey) - err = ks.IPNSKey.ExportToIPFS(name, *forcePublish) - if err != nil { - log.Fatalf("create_and_print_keyset: failed to export keyset: %v", err) - } - log.Infof("create_and_print_keyset: exported IPNSkey to IPFS: %s", ks.IPNSKey.DID) - - d, err := doc.NewFromKeyset(&ks, ks.IPNSKey.DID) - if err != nil { - log.Fatalf("create_and_print_keyset: failed to create DOC: %v", err) - } - - assertionMethod, err := d.GetAssertionMethod() - if err != nil { - log.Fatalf("create_and_print_keyset: failed to get verification method: %v", err) - } - d.Sign(ks.SigningKey, assertionMethod) - - _, err = d.Publish() - if err != nil { - log.Fatalf("create_and_print_keyset: failed to publish DOC: %v", err) - } - log.Debugf("create_and_print_keyset: published DOC: %s", d.ID) - } - - os.Exit(0) -} diff --git a/main.go b/main.go index 4ec035e..79b0032 100644 --- a/main.go +++ b/main.go @@ -4,48 +4,65 @@ import ( "context" "fmt" - "github.com/bahner/go-space/p2p/host" + "github.com/bahner/go-home/actor" + "github.com/bahner/go-home/config" + "github.com/bahner/go-home/room" + "github.com/bahner/go-ma/p2p" "github.com/libp2p/go-libp2p" + log "github.com/sirupsen/logrus" ) +const nodeListenPort = "4001" + func main() { + config.Init() + ctx := context.Background() + ctxTimeout, cancel := context.WithTimeout(ctx, config.GetDiscoveryTimeout()) + defer cancel() + + actorKeyset := config.GetActorKeyset() + roomKeyset := config.GetRoomKeyset() + // cborData, _ := actorKeyset.IPNSKey.MarshalCBOR() + // fmt.Printf("actorKeyset: %s\n", cborData) + // os.Exit(0) + + log.Infof("Intializing actor with identity: %s", actorKeyset.IPNSKey.DID) + + // Conifgure libp2p from here only + libp2pOpts := []libp2p.Option{ + libp2p.ListenAddrStrings(getListenAddrStrings(nodeListenPort)...), + libp2p.Identity(actorKeyset.IPNSKey.PrivKey), + } + + node, ps, err := p2p.Init(ctxTimeout, libp2pOpts...) + if err != nil { + panic(fmt.Sprintf("Failed to initialize p2p: %v", err)) + } - initConfig() - log.Infof("Intializing actor with identity: %s", identity.IPNSKey.DID) - - // Create the node from the keyset. - log.Debug("Creating p2p host from identity ...") - node := host.New() - node.AddOption(libp2p.Identity(identity.IPNSKey.PrivKey)) - // node.AddOption(libp2p.ListenAddrStrings( - // "/ip4/0.0.0.0/tcp/0", - // "/ip4/0.0.0.0/udp/0", - // "/ip6/::/tcp/0", - // "/ip6/::/udp/0")) - log.Debugf("node: %v", node) - // the discoveryProcess return nil, so no need to check. - log.Debug("Initializing subscription service ...") - ps = initSubscriptionService(ctx, node) - - a, err := initActor(identity) + a, err := actor.NewFromKeyset(ctx, ps, actorKeyset, config.GetForcePublish()) if err != nil { panic(fmt.Sprintf("Failed to create actor: %v", err)) } - log.Infof("Actor initialized: %s", a.DID.Fragment) + log.Infof("Actor initialized: %s", a.Entity.DID.Fragment) - // Publish the identity to IPFS. + ra, err := actor.NewFromKeyset(ctx, ps, roomKeyset, config.GetForcePublish()) + if err != nil { + panic(fmt.Sprintf("Failed to create room actor: %v", err)) + } - r, err := NewRoom(room) + r, err := room.New(ra) if err != nil { panic(fmt.Sprintf("Failed to create room: %v", err)) } + log.Debugf("Room initialized: %s", r.Entity.DID.Fragment) - r.Enter(a) + a.Enter(r.Entity.DID.String()) // Draw the UI. - ui := NewChatUI(ctx, r, a) + log.Debugf("Starting text UI") + ui := NewChatUI(ctx, node, ps, r, a) if err := ui.Run(); err != nil { log.Errorf("error running text UI: %s", err) } diff --git a/message.go b/message.go deleted file mode 100644 index 23f1d2a..0000000 --- a/message.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - - "github.com/bahner/go-ma/message" - log "github.com/sirupsen/logrus" -) - -func (a *Actor) Listen(ctx context.Context) { - for { - msg, err := a.From.Next(ctx) - if err != nil { - // Log the error or handle it more gracefully. - log.Errorf("Failed to get next message: %v", err) - return - } - - if msg.ReceivedFrom == ps.Host.Node.ID() { - continue - } - - am := new(message.Message) - if err := json.Unmarshal(msg.Data, am); err != nil { - log.Debugf("Failed to unmarshal message: %v", err) - continue - } - - a.ProcessMessage(am) - } -} - -func (a *Actor) ProcessMessage(m *message.Message) { - // Handle the message according to your application's logic. - // For instance, this could involve updating the actor's state, triggering some action, etc. - log.Debugf("process_message: Processed message: %s", m.ID) -} diff --git a/p2p.go b/p2p.go deleted file mode 100644 index aacb11f..0000000 --- a/p2p.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "context" - "sync" - - "github.com/bahner/go-space/p2p/host" - "github.com/bahner/go-space/p2p/pubsub" -) - -func doDiscovery(ctx context.Context, h *host.Host) error { - - // Start libp2p node and discover peers - h.Init(ctx) - - discoveryWg := &sync.WaitGroup{} - - // Discover peers - // No need to log, as the discovery functions do that. - discoveryWg.Add(1) // Only 1 of the following needs to finish - go host.DiscoverDHTPeers(ctx, discoveryWg, h.Node, rendezvous) - go host.DiscoverMDNSPeers(ctx, discoveryWg, h.Node, rendezvous) - discoveryWg.Wait() - - return nil -} - -func initSubscriptionService(ctx context.Context, h *host.Host) *pubsub.Service { - - doDiscovery(ctx, h) - - // Subscribe to the topic - ps = pubsub.New(h) - ps.Start(ctx) - - return ps -} diff --git a/room.go b/room.go deleted file mode 100644 index f5473c6..0000000 --- a/room.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "crypto/ed25519" - "fmt" - - "github.com/bahner/go-ma/did" - "github.com/bahner/go-ma/did/doc" - "github.com/bahner/go-ma/message" - p2ppupsub "github.com/libp2p/go-libp2p-pubsub" -) - -type Room struct { - Subscription *p2ppupsub.Subscription - Topic *p2ppupsub.Topic - DID *did.DID // NB! "Room must have published it's DIDDocument." - Doc *doc.Document - // The signing and encryption keys are used to verify and encrypt messages. - SigningKey *ed25519.PublicKey - EncryptionKey *ed25519.PublicKey - Messages chan *message.Message - // We can add objects etc here. - Actor *Actor - nick string -} - -// Override ProcessMessage to handle room-specific actions -func (r *Room) ProcessMessage(m *message.Message) { - // Add the message to the Room's Messages channel - r.Messages <- m -} - -func NewRoom(d string) (*Room, error) { - - r := &Room{} - var err error - - // Set the DID - r.DID, err = did.NewFromDID(d) - if err != nil { - return nil, fmt.Errorf("room: failed to create DID: %v", err) - } - - // Set nick to DID fragment - r.nick = r.DID.Fragment - - // Fetch the public keys need to send and receive messages to the room - r.Doc, err = doc.New(r.DID.String(), r.DID.String()) - if err != nil { - return nil, fmt.Errorf("room: failed to create DOC: %v", err) - } - - r.Doc, err = doc.Fetch(r.DID.String()) - if err != nil { - return nil, fmt.Errorf("room: failed to fetch DOC: %v", err) - } - - // Subscribe to the recipients topic - r.Topic, err = ps.Sub.Join(r.DID.String()) - if err != nil { - return nil, fmt.Errorf("failed to create subscription: %v", err) - } - r.Subscription, err = r.Topic.Subscribe() - if err != nil { - return nil, fmt.Errorf("failed to subscribe to topic: %v", err) - } - - // Create a new channel for messages - r.Messages = make(chan *message.Message) - - return r, nil -} - -// func (r *Room) Publish(content string) error { -// m, err := message.New() -// if err != nil { -// k, err := key.NewFromEncodedPrivKey(identity) -// if err != nil { -// return fmt.Errorf("failed to create key: %v", err) -// } -// m.Sign(k.PrivKey) -// msgBytes, err := json.Marshal(m) -// if err != nil { -// return fmt.Errorf("failed to marshal message: %v", err) -// } -// log.Debugf("Publishing message: %s", string(msgBytes)) - -// if err = r.Actor.Topic.Publish(r.Actor.ctx, msgBytes); err != nil { -// return fmt.Errorf("failed to publish message: %v", err) -// } - -// return nil -// } -// } - -// // Example to show how you can use Actor's function in Room. -// func (r *Room) ListPeers() []peer.ID { -// return r.Actor.PubSubService.Sub.ListPeers(r.DID.String()) -// } - -// func (r *Room) JoinRoom() error { -// return r.Actor.InitTopicAndSubscription() -// } - -// func (r *Room) ListenToMessages() { -// for { -// msg, err := r.Actor.sendTopic.Next(r.Actor.ctx) -// if err != nil { -// // Handle error -// log.Errorf("Failed to get next message: %v", err) -// return -// } -// if msg.ReceivedFrom == r.Actor.node.Node.ID() { -// continue -// } - -// roomMessage := new(message.Message) -// if err := json.Unmarshal(msg.Data, roomMessage); err != nil { -// log.Debugf("Failed to unmarshal message: %v", err) -// continue -// } -// r.Messages <- roomMessage -// } -// } - -// // You can further extend the Room's functionalities as needed. - -func (r *Room) Enter(a *Actor) { - - a.ears = r.Messages - -} diff --git a/room/room.go b/room/room.go new file mode 100644 index 0000000..dc74ff9 --- /dev/null +++ b/room/room.go @@ -0,0 +1,17 @@ +package room + +import ( + "github.com/bahner/go-home/actor" +) + +type Room struct { + *actor.Actor +} + +func New(a *actor.Actor) (*Room, error) { + + r := &Room{Actor: a} + + return r, nil + +} diff --git a/ui.go b/ui.go index 3fb8f1b..29badef 100644 --- a/ui.go +++ b/ui.go @@ -8,10 +8,14 @@ import ( "strings" "time" + "github.com/bahner/go-home/actor" + "github.com/bahner/go-home/room" + "github.com/bahner/go-ma/msg" + "github.com/bahner/go-ma/p2p" "github.com/gdamore/tcell/v2" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/host" "github.com/rivo/tview" - - "github.com/bahner/go-ma/message" ) // ChatUI is a Text User Interface (TUI) for a Room. @@ -20,8 +24,10 @@ import ( // chat prompt. type ChatUI struct { ctx context.Context - a *Actor - r *Room + ps *pubsub.PubSub + n host.Host + a *actor.Actor + r *room.Room app *tview.Application peersList *tview.TextView msgBox *tview.TextView @@ -33,14 +39,15 @@ type ChatUI struct { // NewChatUI returns a new ChatUI struct that controls the text UI. // It won't actually do anything until you call Run(). -func NewChatUI(ctx context.Context, r *Room, a *Actor) *ChatUI { +func NewChatUI(ctx context.Context, n host.Host, ps *pubsub.PubSub, r *room.Room, a *actor.Actor) *ChatUI { app := tview.NewApplication() + roomNick := r.Entity.DID.Fragment // make a text view to contain our chat messages msgBox := tview.NewTextView() msgBox.SetDynamicColors(true) msgBox.SetBorder(true) - msgBox.SetTitle(fmt.Sprintf("Room: %s", r.nick)) + msgBox.SetTitle(fmt.Sprintf("Room: %s", roomNick)) // text views are io.Writers, but they don't automatically refresh. // this sets a change handler to force the app to redraw when we get @@ -52,7 +59,7 @@ func NewChatUI(ctx context.Context, r *Room, a *Actor) *ChatUI { // an input field for typing messages into inputCh := make(chan string, 32) input := tview.NewInputField(). - SetLabel(r.nick + " > "). + SetLabel(roomNick + " > "). SetFieldWidth(0). SetFieldBackgroundColor(tcell.ColorBlack) @@ -102,6 +109,9 @@ func NewChatUI(ctx context.Context, r *Room, a *Actor) *ChatUI { return &ChatUI{ ctx: ctx, + n: n, + ps: ps, + r: r, a: a, app: app, peersList: peersList, @@ -143,7 +153,7 @@ func (ui *ChatUI) end() { // displayChatMessage writes a ChatMessage from the room to the message window, // with the sender's nick highlighted in green. -func (ui *ChatUI) displayChatMessage(cm *message.Message) { +func (ui *ChatUI) displayChatMessage(cm *msg.Message) { prompt := withColor("green", fmt.Sprintf("<%s>:", cm.From)) fmt.Fprintf(ui.msgW, "%s %s\n", prompt, cm.Body) } @@ -151,7 +161,7 @@ func (ui *ChatUI) displayChatMessage(cm *message.Message) { // displaySelfMessage writes a message from ourself to the message window, // with our nick highlighted in yellow. func (ui *ChatUI) displaySelfMessage(msg string) { - prompt := withColor("yellow", fmt.Sprintf("<%s>:", ui.r.nick)) + prompt := withColor("yellow", fmt.Sprintf("<%s>:", ui.r.Entity.DID.Fragment)) fmt.Fprintf(ui.msgW, "%s %s\n", prompt, msg) } @@ -171,7 +181,7 @@ func (ui *ChatUI) handleEvents() { ui.handleChatMessage(input) } - case m := <-ui.a.ears: + case m := <-ui.a.Messages: ui.displayChatMessage(m) // case <-peerRefreshTicker.C: @@ -196,8 +206,6 @@ func (ui *ChatUI) handleCommands(input string) { ui.triggerDiscovery() case "/enter": ui.handleEnterCommand(args) - case "/nick": - ui.handleNickCommand(args) case "/refresh": ui.app.Draw() default: @@ -213,16 +221,6 @@ func (ui *ChatUI) handleEnterCommand(args []string) { } } -func (ui *ChatUI) handleNickCommand(args []string) { - - if len(args) > 1 { - nick := args[1] - ui.r.nick = nick - } else { - ui.displaySystemMessage("Usage: /enter [new_topic_name]") - } -} - func (ui *ChatUI) handleStatusCommand(args []string) { if len(args) > 1 { switch args[1] { @@ -244,14 +242,14 @@ func (ui *ChatUI) handleStatusCommand(args []string) { } func (ui *ChatUI) handleChatMessage(input string) error { - // Wrapping the string message into the message.Message structure + // Wrapping the string message into the msg.Message structure msgBytes, err := json.Marshal(input) if err != nil { return fmt.Errorf("message serialization error: %s", err) } - msg, err := message.New(ui.r.nick, ui.r.nick, string(msgBytes), "application/json") + msg, err := msg.New(ui.r.Entity.DID.Fragment, ui.r.Entity.DID.Fragment, string(msgBytes), "application/json") if err != nil { return fmt.Errorf("message creation error: %s", err) } @@ -262,7 +260,7 @@ func (ui *ChatUI) handleChatMessage(input string) error { return fmt.Errorf("message serialization error: %s", err) } - err = ui.r.Topic.Publish(ui.ctx, m) + err = ui.r.Public.Publish(ui.ctx, m) if err != nil { return fmt.Errorf("publish error: %s", err) } @@ -294,7 +292,7 @@ func withColor(color, msg string) string { // // Return whatever status you'd like about the topic. // // Fetching peers as an example below: // peers := ui.r.Topic.ListPeers() -// return fmt.Sprintf("Topic Status: %s | Peers: %v", ui.r.nick, peers) +// return fmt.Sprintf("Topic Status: %s | Peers: %v", ui.r.Entity.DID.Fragment, peers) // } // func (ui *ChatUI) getStatusHost() string { @@ -305,7 +303,8 @@ func withColor(color, msg string) string { func (ui *ChatUI) triggerDiscovery() { - go ps.Host.StartPeerDiscovery(ui.ctx, rendezvous, serviceName) + // go ui.n.StartPeerDiscovery(ui.ctx, config.GetRendezvous()) + p2p.StartPeerDiscovery(ui.ctx, ui.n) ui.displaySystemMessage("Discovery process started...") } @@ -317,18 +316,18 @@ func (ui *ChatUI) displaySystemMessage(msg string) { func (ui *ChatUI) changeTopic(d string) { // Create a new Room instance with the new topic - room, err := NewRoom(d) + room, err := room.New(ui.a) if err != nil { - ui.displaySystemMessage(fmt.Sprintf("Failed to create the new topic '%s': %s", d, err)) + ui.displaySystemMessage(fmt.Sprintf("Failed to enter room '%s': %s", d, err)) return } // If successful, assign the new Room instance to ui.cr ui.r = room // ui.msgW.SetTitle(fmt.Sprintf("Topic: %s", newTopic)) - ui.msgBox.SetTitle("Room: " + ui.r.nick) + ui.msgBox.SetTitle("Room: " + ui.r.Entity.DID.Fragment) // Notify the user - ui.displaySystemMessage(fmt.Sprintf("Entered the new Room: %s", ui.r.nick)) + ui.displaySystemMessage(fmt.Sprintf("Entered the new Room: %s", ui.r.Entity.DID.Fragment)) ui.app.Draw() }