From f4e7517622ed119f939569010f4fc9ecbfb030dd Mon Sep 17 00:00:00 2001 From: Kleissner Date: Fri, 26 Feb 2021 13:29:00 +0100 Subject: [PATCH] Initial command line client. --- .gitignore | 4 + Command Line.go | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ Main.go | 15 ++++ README.md | 23 ++++++ Test_test.go | 111 ++++++++++++++++++++++++++ 5 files changed, 354 insertions(+) create mode 100644 .gitignore create mode 100644 Command Line.go create mode 100644 Main.go create mode 100644 README.md create mode 100644 Test_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fbf3ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.exe +log.txt +*debug_bin +*.yaml diff --git a/Command Line.go b/Command Line.go new file mode 100644 index 0000000..a50662f --- /dev/null +++ b/Command Line.go @@ -0,0 +1,201 @@ +/* +File Name: Command Line.go +Copyright: 2021 Peernet Foundation s.r.o. +Author: Peter Kleissner +*/ + +package main + +import ( + "bufio" + "encoding/hex" + "fmt" + "net" + "os" + "strconv" + "strings" + "time" + + "github.com/PeernetOfficial/core" +) + +func getUserOptionString(reader *bufio.Reader) (response string, valid bool) { + responseA, err := reader.ReadString('\n') + if err != nil { + return "", false + } + + responseA = strings.TrimSpace(responseA) // also removes the delimiter + + return responseA, true +} + +func getUserOptionBool(reader *bufio.Reader) (response bool, valid bool) { + responseA, err := reader.ReadString('\n') + if err != nil { + return false, false + } + + responseA = strings.TrimSpace(responseA) // also removes the delimiter + + responseI, err := strconv.Atoi(responseA) + if err != nil || (responseI != 0 && responseI != 1) { + return false, false + } + + return responseI == 1, true +} + +func getUserOptionInt(reader *bufio.Reader) (response int, valid bool) { + responseA, err := reader.ReadString('\n') + if err != nil { + return 0, false + } + + responseA = strings.TrimSpace(responseA) // also removes the delimiter + + responseI, err := strconv.Atoi(responseA) + if err != nil { + return 0, false + } + + return responseI, true +} + +func showHelp() { + fmt.Print("Please enter a command:\n") + fmt.Print("help Show this help\n" + + "net list Lists all network adapters and their IPs\n" + + "status Get current status\n" + + "chat Send text to all peers\n" + + "peer list List current peers\n" + + "debug key create Create Public-Private Key pair\n" + + "debug key self List current Public-Private Key pair\n" + + "\n") +} + +func userCommands() { + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Peernet Cmd " + core.Version + "\n------------------------------\n") + showHelp() + + for { + command, valid := getUserOptionString(reader) + if !valid { + time.Sleep(time.Second) + continue + } + + switch command { + case "help", "?": + showHelp() + + case "net list": + fmt.Print(NetworkListOutput()) + + case "debug key create": + privateKey, publicKey, err := core.Secp256k1NewPrivateKey() + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + + fmt.Printf("Private Key: %s\n", hex.EncodeToString(privateKey.Serialize())) + fmt.Printf("Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed())) + + case "debug key self": + privateKey, publicKey := core.ExportPrivateKey() + fmt.Printf("Private Key: %s\n", hex.EncodeToString(privateKey.Serialize())) + fmt.Printf("Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed())) + + case "peer list": + for _, peer := range core.PeerlistGet() { + fmt.Printf("* %s\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed())) + if len(peer.Connections) > 0 { + fmt.Printf(" Connections:\n") + for _, connection := range peer.Connections { + address, _, _ := connection.Network.GetListen() + fmt.Printf(" %-30s on adapter %s\n", connection.Address.String(), address.String()) + } + } else { + fmt.Printf(" Connections: [none]\n") + } + fmt.Printf(" Packets sent: %d\n", peer.StatsPacketSent) + fmt.Printf(" Packets received: %d\n", peer.StatsPacketReceived) + } + + case "chat all", "chat": + if text, valid := getUserOptionString(reader); valid { + core.SendChatAll(text) + } + + case "status": + _, publicKey := core.ExportPrivateKey() + fmt.Printf("----------------\nPublic Key: %s\n\n", hex.EncodeToString(publicKey.SerializeCompressed())) + + fmt.Printf("Listen Address Multicast IP out\n") + + for _, network := range core.GetNetworks(4) { + address, _, broadcastIPv4 := network.GetListen() + fmt.Printf("%-30s\n", address.String()) + + for _, broadcastIP := range broadcastIPv4 { + fmt.Printf(" %-30s\n", broadcastIP.String()) + } + } + for _, network := range core.GetNetworks(6) { + address, multicastIP, _ := network.GetListen() + fmt.Printf("%-30s %-30s\n", address.String(), multicastIP.String()) + } + + fmt.Printf("\nPeer ID IP Sent Received\n") + for _, peer := range core.PeerlistGet() { + fmt.Printf("%-66s %-30s %-8d %-8d\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), peer.Connections[0].Address.String(), peer.StatsPacketSent, peer.StatsPacketReceived) + } + + fmt.Printf("\n") + } + } +} + +// NetworkListOutput provides a user friendly output +func NetworkListOutput() (text string) { + + interfaceList, err := net.Interfaces() + if err != nil { + return "Error " + err.Error() + } + + // iterate through all interfaces + for _, ifaceSingle := range interfaceList { + text += "Interface " + ifaceSingle.Name + ":\n" + //text += " MAC: " + ifaceSingle.HardwareAddr.String() + "\n" + + addresses, err := ifaceSingle.Addrs() + if err != nil { + text += " Error getting addresses: " + err.Error() + "\n\n" + continue + } + + // iterate through all IPs of the interfaces + for _, address := range addresses { + text += " IP: " + address.(*net.IPNet).IP.String() + "\n" + } + + // Subscribed Multicast IPs of adapters are not really newsworthy + //addresses2, err := ifaceSingle.MulticastAddrs() + //if err != nil { + // text += " Error getting multicast addresses: " + err.Error() + "\n\n" + // continue + //} + + //for _, address := range addresses2 { + // text += " Multicast: " + address.(*net.IPAddr).IP.String() + "\n" + //} + + text += "\n" + } + + return text +} diff --git a/Main.go b/Main.go new file mode 100644 index 0000000..dcd9895 --- /dev/null +++ b/Main.go @@ -0,0 +1,15 @@ +/* +File Name: Main.go +Copyright: 2021 Peernet Foundation s.r.o. +Author: Peter Kleissner +*/ + +package main + +import "github.com/PeernetOfficial/core" + +func main() { + core.Connect() + + userCommands() +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2de2ef --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Peernet Command Line Client + +This is the command line client used for testing, debug and development purposes. It uses the [core library](https://github.com/PeernetOfficial/core). Check the core library for optional settings. + +This client can be used as root peer to help speed up discovery of peers and data. + +## Compile + +First get all the dependencies. Below list contains both dependencies from the core package and this tool. + +``` +go get -u github.com/PeernetOfficial/core +go get -u github.com/gofrs/uuid +go get -u github.com/btcsuite/btcd/btcec +go get -u github.com/libp2p/go-reuseport +go get -u lukechampine.com/blake3 +``` + +To build: + +``` +go build +``` diff --git a/Test_test.go b/Test_test.go new file mode 100644 index 0000000..f507448 --- /dev/null +++ b/Test_test.go @@ -0,0 +1,111 @@ +// Random tests for debugging. Not actual functionality tests. +package main + +import ( + "crypto/rand" + "fmt" + "testing" + "time" + + "github.com/PeernetOfficial/core" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "lukechampine.com/blake3" +) + +func TestSignCompact(t *testing.T) { + privateKey, publicKey, err := core.Secp256k1NewPrivateKey() + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + + var compact1 []byte + data := []byte{1, 2, 3} + + time1 := time.Now() + + compact1 = make([]byte, 0) + compact1, err = btcec.SignCompact(btcec.S256(), privateKey, chainhash.DoubleHashB(data), true) + + timeT := time.Since(time1) + fmt.Printf("length %d time %s\n", len(compact1), timeT.String()) + + time2 := time.Now() + + key2, _, err := btcec.RecoverCompact(btcec.S256(), compact1, chainhash.DoubleHashB(data)) + + timeT2 := time.Since(time2) + fmt.Printf("VALID PUBLIC KEY: %t\n", key2.IsEqual(publicKey)) + fmt.Printf("time %s\n", timeT2.String()) +} + +func TestEncryption1(t *testing.T) { + privateKey, publicKey, err := core.Secp256k1NewPrivateKey() + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + + packet1 := packetRaw{Protocol: 0, Command: 1, Payload: []byte{1, 2, 3}} + + raw1, err := packetEncrypt(privateKey, publicKey, &packet1) + if err != nil { + return + } + + packet1d, _, err := packetDecrypt(raw1, publicKey) + if err != nil { + fmt.Printf("Error %s\n", err.Error()) + return + } + + fmt.Printf("%d\n", packet1d.Command) +} + +func BenchmarkHash(b *testing.B) { + length := 20000 + + b.Run("blake3", func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(int64(length)) + buf := make([]byte, length) + rand.Read(buf) + for i := 0; i < b.N; i++ { + blake3.Sum256(buf) + } + }) + b.Run("SHA256 double", func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(int64(length)) + buf := make([]byte, length) + rand.Read(buf) + for i := 0; i < b.N; i++ { + chainhash.DoubleHashB(buf) + } + }) +} + +func TestEncryption2(t *testing.T) { + privateKey, publicKey, err := core.Secp256k1NewPrivateKey() + if err != nil { + fmt.Printf("Error: %s\n", err.Error()) + return + } + + packet1 := packetRaw{Protocol: 0, Command: 0} + + for n := 0; n < 1000; n++ { + raw1, err := packetEncrypt(privateKey, publicKey, &packet1) + if err != nil { + return + } + packet1d, _, err := packetDecrypt(raw1, publicKey) + if err != nil { + fmt.Printf("Error %s\n", err.Error()) + return + } + fmt.Printf("Command %d payload %v\n", packet1d.Command, packet1d.Payload) + } + +}