waku-org/go-waku

View on GitHub
examples/noise/main.go

Summary

Maintainability
A
2 hrs
Test Coverage
package main

import (
    "context"
    "encoding/hex"
    "fmt"
    "net"
    "net/url"
    "noise/pb"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"

    logging "github.com/ipfs/go-log/v2"
    "github.com/libp2p/go-libp2p/core/peer"
    n "github.com/waku-org/go-noise"
    "github.com/waku-org/go-waku/waku/v2/dnsdisc"
    "github.com/waku-org/go-waku/waku/v2/node"
    "github.com/waku-org/go-waku/waku/v2/protocol"
    "github.com/waku-org/go-waku/waku/v2/protocol/noise"
    "github.com/waku-org/go-waku/waku/v2/protocol/relay"
    "github.com/waku-org/go-waku/waku/v2/timesource"
    "github.com/waku-org/go-waku/waku/v2/utils"
    "go.uber.org/zap"
    "google.golang.org/protobuf/proto"
)

var log = utils.Logger().Named("noise")

func main() {
    // Removing noisy logs
    lvl, err := logging.LevelFromString("error")
    if err != nil {
        panic(err)
    }
    logging.SetAllLoggers(lvl)

    hostAddr, _ := net.ResolveTCPAddr("tcp", fmt.Sprint("0.0.0.0:0"))

    wakuNode, err := node.New(
        node.WithHostAddress(hostAddr),
        node.WithNTP(),
        node.WithWakuRelay(),
    )
    if err != nil {
        log.Error("could not instantiate waku", zap.Error(err))
        return
    }

    if err := wakuNode.Start(context.Background()); err != nil {
        log.Error("could not start waku", zap.Error(err))
        return
    }

    discoverFleetNodes(wakuNode)

    myStaticKey, _ := n.DH25519.GenerateKeypair()
    myEphemeralKey, _ := n.DH25519.GenerateKeypair()

    relayMessenger, err := noise.NewWakuRelayMessenger(context.Background(), wakuNode.Relay(), nil, timesource.NewDefaultClock())
    if err != nil {
        log.Error("could not create relay messenger", zap.Error(err))
        return
    }

    pairingObj, err := noise.NewPairing(myStaticKey, myEphemeralKey, noise.WithDefaultResponderParameters(), relayMessenger, utils.Logger())
    if err != nil {
        log.Error("could not create pairing object", zap.Error(err))
        return
    }

    qrString, qrMessageNameTag := pairingObj.PairingInfo()
    qrURL := "messageNameTag=" + url.QueryEscape(hex.EncodeToString(qrMessageNameTag[:])) + "&qrCode=" + url.QueryEscape(qrString)

    wg := sync.WaitGroup{}

    // Execute in separate go routine
    wg.Add(1)
    go func() {
        defer wg.Done()

        ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
        defer cancel()
        err := pairingObj.Execute(ctx)
        if err != nil {
            log.Error("could not perform handshake", zap.Error(err))
            return
        }
    }()

    // Confirmation is done by manually
    wg.Add(1)
    go func() {
        defer wg.Done()
        authCode := <-pairingObj.AuthCode()
        fmt.Println("=============================================")
        fmt.Println("TODO: ask to confirm pairing. Automaticaly confirming authcode", authCode)
        fmt.Println("=============================================")
        err := pairingObj.ConfirmAuthCode(true)
        if err != nil {
            log.Error("could not confirm authcode", zap.Error(err))
            return
        }
    }()

    fmt.Println("=============================================================================")
    fmt.Printf("Browse https://examples.waku.org/noise-js/?%s\n", qrURL)
    fmt.Println("=============================================================================")

    wg.Wait()

    // Securely transferring messages
    if pairingObj.HandshakeComplete() {
        go writeLoop(context.Background(), wakuNode, pairingObj)
        go readLoop(context.Background(), wakuNode, pairingObj)
    }

    // Wait for a SIGINT or SIGTERM signal
    ch := make(chan os.Signal, 1)
    signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
    <-ch
    fmt.Println("\n\n\nReceived signal, shutting down...")

    // shut the node down
    wakuNode.Stop()

}

func discoverFleetNodes(wakuNode *node.WakuNode) error {
    log.Info("Connecting to test fleet...")

    dnsDiscoveryUrl := "enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im"
    nodes, err := dnsdisc.RetrieveNodes(context.Background(), dnsDiscoveryUrl)
    if err != nil {
        return err
    }
    var peerInfo []peer.AddrInfo
    for _, n := range nodes {
        peerInfo = append(peerInfo, n.PeerInfo)
    }

    wg := sync.WaitGroup{}
    wg.Add(len(peerInfo))
    for _, p := range peerInfo {
        go func(p peer.AddrInfo) {
            defer wg.Done()

            ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second)
            defer cancel()
            err = wakuNode.DialPeerWithInfo(ctx, p)
            if err != nil {
                log.Error("could not connect", zap.String("peerID", p.ID.String()), zap.Error(err))
            } else {
                log.Info("connected", zap.String("peerID", p.ID.String()))
            }
        }(p)

    }
    wg.Wait()
    return nil
}

func writeLoop(ctx context.Context, wakuNode *node.WakuNode, pairingObj *noise.Pairing) {
    cnt := 0
    for {
        time.Sleep(4 * time.Second)

        cnt++
        chatMessage := &pb.Chat2Message{
            Timestamp: uint64(wakuNode.Timesource().Now().Unix()),
            Nick:      "go-waku",
            Payload:   []byte(fmt.Sprintf("Hello World! #%d", cnt)),
        }

        chatMessageBytes, err := proto.Marshal(chatMessage)
        if err != nil {
            log.Error("Error encoding message: ", zap.Error(err))
            continue
        }

        msg, err := pairingObj.Encrypt(chatMessageBytes)
        if err != nil {
            log.Error("error encrypting message", zap.Error(err))
            continue
        }

        msg.Timestamp = utils.GetUnixEpoch(wakuNode.Timesource())

        _, err = wakuNode.Relay().Publish(ctx, msg, relay.WithDefaultPubsubTopic())
        if err != nil {
            log.Error("Error sending a message", zap.Error(err))
        }
    }
}

func readLoop(ctx context.Context, wakuNode *node.WakuNode, pairingObj *noise.Pairing) {
    sub, err := wakuNode.Relay().Subscribe(ctx, protocol.NewContentFilter(relay.DefaultWakuTopic))
    if err != nil {
        log.Error("Could not subscribe", zap.Error(err))
        return
    }

    for value := range sub[0].Ch {
        if value.Message().ContentTopic != pairingObj.ContentTopic {
            continue
        }

        msgBytes, err := pairingObj.Decrypt(value.Message())
        if err != nil {
            log.Debug("Error decoding a message", zap.Error(err))
            continue
        }

        msg := &pb.Chat2Message{}
        if err := proto.Unmarshal(msgBytes, msg); err != nil {
            log.Error("Error decoding a message", zap.Error(err))
            continue
        }

        fmt.Println("Received msg from ", msg.Nick, " - "+string(msg.Payload))
    }
}