go-waku/examples/chat2/main.go

259 lines
7.0 KiB
Go
Raw Normal View History

2021-04-04 17:06:17 +00:00
package main
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
mrand "math/rand"
"net"
"net/http"
"os"
"time"
"github.com/ethereum/go-ethereum/crypto"
logging "github.com/ipfs/go-log"
2021-04-04 17:06:17 +00:00
"github.com/libp2p/go-libp2p-core/peer"
"github.com/status-im/go-waku/waku/v2/node"
2021-04-22 00:12:51 +00:00
"github.com/status-im/go-waku/waku/v2/protocol/store"
2021-04-04 17:06:17 +00:00
)
var DefaultContentTopic string = "/toy-chat/2/huilong/proto"
2021-04-04 17:06:17 +00:00
func main() {
mrand.Seed(time.Now().UTC().UnixNano())
nickFlag := flag.String("nick", "", "nickname to use in chat. will be generated if empty")
fleetFlag := flag.String("fleet", "wakuv2.prod", "Select the fleet to connect to. (wakuv2.prod, wakuv2.test)")
contentTopicFlag := flag.String("contenttopic", DefaultContentTopic, "content topic to use for the chat")
2021-06-28 14:14:28 +00:00
nodeKeyFlag := flag.String("nodekey", "", "private key for this node. will be generated if empty")
staticNodeFlag := flag.String("staticnode", "", "connects to a node. will get a random node from fleets.status.im if empty")
relayFlag := flag.Bool("relay", true, "enable relay protocol")
storeNodeFlag := flag.String("storenode", "", "connects to a store node to retrieve messages. will get a random node from fleets.status.im if empty")
2021-04-04 17:06:17 +00:00
port := flag.Int("port", 0, "port. Will be random if 0")
payloadV1Flag := flag.Bool("payloadV1", false, "use Waku v1 payload encoding/encryption. default false")
2021-06-28 14:14:28 +00:00
filterFlag := flag.Bool("filter", false, "enable filter protocol")
filterNodeFlag := flag.String("filternode", "", "multiaddr of peer to to request content filtering of messages")
lightPushFlag := flag.Bool("lightpush", false, "enable lightpush protocol")
lightPushNodeFlag := flag.String("lightpushnode", "", "Multiaddr of peer to to request lightpush of published messages")
keepAliveFlag := flag.Int64("keep-alive", 300, "interval in seconds for pinging peers to keep the connection alive.")
2021-06-28 14:14:28 +00:00
2021-04-04 17:06:17 +00:00
flag.Parse()
hostAddr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", *port))
if *fleetFlag != "wakuv2.prod" && *fleetFlag != "wakuv2.test" {
fmt.Println("Invalid fleet. Valid values are wakuv2.prod and wakuv2.test")
return
}
2021-04-04 17:06:17 +00:00
// use the nickname from the cli flag, or a default if blank
nodekey := *nodeKeyFlag
if len(nodekey) == 0 {
var err error
nodekey, err = randomHex(32)
if err != nil {
fmt.Println("Could not generate random key")
return
}
}
prvKey, err := crypto.HexToECDSA(nodekey)
ctx := context.Background()
2021-06-28 14:14:28 +00:00
opts := []node.WakuNodeOption{
node.WithPrivateKey(prvKey),
node.WithHostAddress([]net.Addr{hostAddr}),
node.WithWakuStore(false),
2021-06-28 14:14:28 +00:00
node.WithKeepAlive(time.Duration(*keepAliveFlag) * time.Second),
}
if *relayFlag {
opts = append(opts, node.WithWakuRelay())
}
if *filterFlag {
opts = append(opts, node.WithWakuFilter())
}
if *lightPushFlag || *lightPushNodeFlag != "" {
*lightPushFlag = true // If a lightpushnode was set and lightpush flag was false
opts = append(opts, node.WithLightPush())
}
wakuNode, err := node.New(ctx, opts...)
2021-04-04 17:06:17 +00:00
if err != nil {
fmt.Print(err)
return
}
2021-06-28 14:14:28 +00:00
if *lightPushNodeFlag != "" && *lightPushFlag {
wakuNode.AddLightPushPeer(*lightPushNodeFlag)
}
if *filterNodeFlag != "" && *filterFlag {
wakuNode.AddFilterPeer(*filterNodeFlag)
}
2021-04-04 17:06:17 +00:00
// use the nickname from the cli flag, or a default if blank
nick := *nickFlag
if len(nick) == 0 {
nick = defaultNick(wakuNode.Host().ID())
}
// join the chat
2021-06-28 14:14:28 +00:00
chat, err := NewChat(ctx, wakuNode, wakuNode.Host().ID(), *contentTopicFlag, *payloadV1Flag, *lightPushFlag, nick)
2021-04-04 17:06:17 +00:00
if err != nil {
panic(err)
}
// Display panic level to reduce log noise
lvl, err := logging.LevelFromString("panic")
if err != nil {
panic(err)
}
logging.SetAllLoggers(lvl)
2021-04-22 00:12:51 +00:00
ui := NewChatUI(ctx, chat)
2021-04-04 17:06:17 +00:00
// Connect to a static node or use random node from fleets.status.im
go func() {
time.Sleep(200 * time.Millisecond)
staticnode := *staticNodeFlag
storenode := *storeNodeFlag
2021-04-15 02:22:18 +00:00
var fleetData []byte
if len(staticnode) == 0 || len(storenode) == 0 {
fleetData = getFleetData()
}
2021-04-04 17:06:17 +00:00
if len(staticnode) == 0 {
ui.displayMessage(fmt.Sprintf("No static peers configured. Choosing one at random from %s fleet...", *fleetFlag))
staticnode = getRandomFleetNode(fleetData, *fleetFlag)
2021-04-04 17:06:17 +00:00
}
err = wakuNode.DialPeer(staticnode)
if err != nil {
ui.displayMessage("Could not connect to peer: " + err.Error())
return
} else {
ui.displayMessage("Connected to peer: " + staticnode)
}
if len(storenode) == 0 {
ui.displayMessage(fmt.Sprintf("No store node configured. Choosing one at random from %s fleet...", *fleetFlag))
storenode = getRandomFleetNode(fleetData, *fleetFlag)
}
2021-04-15 02:22:18 +00:00
storeNodeId, err := wakuNode.AddStorePeer(storenode)
if err != nil {
ui.displayMessage("Could not connect to storenode: " + err.Error())
return
} else {
ui.displayMessage("Connected to storenode: " + storenode)
}
2021-04-12 18:03:58 +00:00
time.Sleep(300 * time.Millisecond)
ui.displayMessage("Querying historic messages")
2021-04-15 02:22:18 +00:00
tCtx, _ := context.WithTimeout(ctx, 5*time.Second)
response, err := wakuNode.Query(tCtx, []string{*contentTopicFlag}, 0, 0,
2021-04-15 02:22:18 +00:00
store.WithAutomaticRequestId(),
2021-04-15 17:57:18 +00:00
store.WithPeer(*storeNodeId),
2021-04-15 02:22:18 +00:00
store.WithPaging(true, 0))
if err != nil {
2021-04-12 18:03:58 +00:00
ui.displayMessage("Could not query storenode: " + err.Error())
} else {
chat.displayMessages(response.Messages)
}
2021-04-04 17:06:17 +00:00
}()
//draw the UI
2021-04-04 17:06:17 +00:00
if err = ui.Run(); err != nil {
printErr("error running text UI: %s", err)
}
2021-06-28 14:14:28 +00:00
wakuNode.Stop()
// TODO: filter unsubscribeAll
2021-04-04 17:06:17 +00:00
}
// Generates a random hex string with a length of n
func randomHex(n int) (string, error) {
bytes := make([]byte, n)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
// printErr is like fmt.Printf, but writes to stderr.
func printErr(m string, args ...interface{}) {
fmt.Fprintf(os.Stderr, m, args...)
}
// defaultNick generates a nickname based on the $USER environment variable and
// the last 8 chars of a peer ID.
func defaultNick(p peer.ID) string {
return fmt.Sprintf("%s-%s", os.Getenv("USER"), shortID(p))
}
// shortID returns the last 8 chars of a base58-encoded peer id.
func shortID(p peer.ID) string {
pretty := p.Pretty()
return pretty[len(pretty)-8:]
}
func getFleetData() []byte {
2021-04-04 17:06:17 +00:00
url := "https://fleets.status.im"
httpClient := http.Client{
Timeout: time.Second * 2,
}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
res, getErr := httpClient.Do(req)
if getErr != nil {
log.Fatal(getErr)
}
if res.Body != nil {
defer res.Body.Close()
}
body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}
return body
}
func getRandomFleetNode(data []byte, fleetId string) string {
2021-04-04 17:06:17 +00:00
var result map[string]interface{}
json.Unmarshal(data, &result)
2021-04-04 17:06:17 +00:00
fleets := result["fleets"].(map[string]interface{})
fleet := fleets[fleetId].(map[string]interface{})
waku := fleet["waku"].(map[string]interface{})
2021-04-04 17:06:17 +00:00
var wakunodes []string
for v := range waku {
wakunodes = append(wakunodes, v)
break
}
randKey := wakunodes[mrand.Intn(len(wakunodes))]
return waku[randKey].(string)
}