status-research/hello-pss/hello_pss.go

738 lines
23 KiB
Go

package main
import (
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
"github.com/ethereum/go-ethereum/swarm/pss"
"context"
"bufio"
"fmt"
"crypto/ecdsa"
"os"
"bytes"
"time"
"io/ioutil"
"encoding/json"
"github.com/ethereum/go-ethereum/log"
"strconv"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/swarm/storage/feed"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm"
// "github.com/ethereum/go-ethereum/swarm/network"
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
// XXX: Shouldn't this be bzzclient?
feedsapi "github.com/ethereum/go-ethereum/swarm/api/client"
)
var (
// logger
Log = log.New("hello-pss", "*")
// XXX: Should be multiple, but cheating and only taking parent0
lastParent = ""
// Set of seen parent0 swarm hash messages
SeenSet = make(map[string]bool)
)
// TODO: Ensure node starts in light node so it doesn't eat up a lot of disk space
// XXX: Warning, this is bad design. Should use keystore for this.
func getHexPrivateKey() string {
privateKey, err := crypto.GenerateKey()
if err != nil {
Log.Crit("can't generate private key", err)
}
privateKeyBytes := crypto.FromECDSA(privateKey)
// Debugging and basic key operations
//publicKey := privateKey.Public()
//publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
//if !ok {
// log.Crit("error casting public key to ECDSA", err)
//}
//publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
//address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
//fmt.Println("Private Key: ", hexutil.Encode(privateKeyBytes))
//fmt.Println("Public Key: ", hexutil.Encode(publicKeyBytes[4:]))
//fmt.Println("Address: ", address)
return hexutil.Encode(privateKeyBytes)
}
func getPrivateKeyFromFile(keyfile string) *ecdsa.PrivateKey {
contents, err := ioutil.ReadFile(keyfile)
if err != nil {
log.Crit("Unable to read keyfile", keyfile)
}
println(string(contents))
privateKeyBytes, err := hexutil.Decode(string(contents))
if err != nil {
log.Crit("Unable to get private key bytes", err)
}
privateKey, err := crypto.ToECDSA(privateKeyBytes)
if err != nil {
log.Crit("Unable to get private key", err)
}
return privateKey
}
// XXX: This shouldn't be needed
func getp2pConfig(listenaddr string) p2p.Config {
return p2p.Config{
ListenAddr: listenaddr,
MaxPeers: 25,
NAT: nat.Any(),
}
}
// Create a node
func newNode(port int) (*node.Node, error) {
cfg := &node.DefaultConfig
cfg.DataDir = fmt.Sprintf("%s%d", ".data_", port)
// XXX: cfg.P2P.ListenAddr = fmt.Sprintf(":%d", port), should work
if port == 9600 {
cfg.P2P = getp2pConfig(":30400")
} else if port == 9601 {
cfg.P2P = getp2pConfig(":30401")
} else if port == 9602 {
cfg.P2P = getp2pConfig(":30402")
} else {
log.Crit("Ports be fucked up", "yeah")
}
cfg.HTTPPort = port
cfg.IPCPath = "bzz.ipc"
//fmt.Printf("Current data directory is %s\n", cfg.DataDir)
return node.New(cfg)
}
// TODO: How do we know this
// XXX: We can probably cheat with in-memory only by downloading all messages and A is restarting
func seen(hash string) bool {
return SeenSet[hash]
}
func fetchMessage(hash string) {
// download from swarm
httpClient := feedsapi.NewClient("http://localhost:9602") // XXX 9601
response, _, err := httpClient.DownloadRaw(hash)
if err != nil {
fmt.Println("Unable to download raw", err)
os.Exit(1)
}
// Assuming no error, save hash to SeenSet so we don't download again
// XXX: Edge case because message id of pss message not seen, so calculate again?
SeenSet[hash] = true
buf := new(bytes.Buffer)
buf.ReadFrom(response)
str := buf.String()
msg := deserialize(str)
//fmt.Println("***Download raw", str)
handleMessage(msg)
}
// XXX: Assumes no duplicates
func handleMessage(msg message) {
// Cheating
// XXX so much duplication eh
httpClient := feedsapi.NewClient("http://localhost:9602") // XXX 9601
// How many cases are there?
// We have all the dependencies
// We need to fetch more dependencies
// We have seen it before
// We have not seen it before
// XXX: Edge case, incoming message don't have hash stored already
// XXX: Hack work around, upload message and check if hash is same
// Should be possible to figure out hash locally but yeah
// XXX: Assuming hash is same, which it should be
payload := serialize(msg)
hash, err := httpClient.UploadRaw(bytes.NewReader(payload), int64(len(payload)), false)
if err != nil {
fmt.Println("Unable to upload raw", err)
os.Exit(1)
}
//fmt.Println("** Adding hash of message to SeenSet", hash)
SeenSet[hash] = true
// Interesting, this seems to unroll and deal with order automatically
// XXX: Edge cases here because it doesn't save messages, just happy path stack
parent0 := msg.Parents[0]
if (parent0 != "") && !seen(parent0) {
fmt.Printf("[Unmet dependency, downloading: %s]\n", parent0)
fetchMessage(parent0)
}
// Only print it if all dependencies are met
fmt.Println("Alice:", string(msg.Text))
// fmt.Println("Alice:", string(msg.Text), "- parent0:", string(msg.Parents[0]))
// fmt.Println("Feed result: ", msg.Text, "- parent0:", msg.Parents[0])
}
func listenForMessages(msgC chan pss.APIMsg) {
for {
in := <-msgC
// XXX: Who is in.key really? want readable public key here
// XXX: The UX is an illusion
// XXX: parsing logic should be same for pull from feed
parsed := deserialize(string(in.Msg))
//fmt.Println("Alice old:", string(in.Msg))
// XXX Only one parent
// TODO: Get all the parents here
handleMessage(parsed)
//fmt.Println("\nReceived message", string(in.Msg), "from", fmt.Sprintf("%x", in.Key))
}
}
// TODO: Error handling if fail
func postToFeed(client *rpc.Client, signer *feed.GenericSigner, receiver string, topic string, data []byte) {
// For creating manifest, then posting, then finally getting
// Create a new feed with user and topic.
f := new(feed.Feed)
f.User = signer.Address()
f.Topic, _ = feed.NewTopic("bob", nil)
query := feed.NewQueryLatest(f, lookup.NoClue)
// XXX: Ok feeds seems fairly broken
//httpClient := feedsapi.NewClient("https://swarm-gateways.net")
// Cheating
httpClient := feedsapi.NewClient("http://localhost:9602") // XXX 9600
// local sender alice
//httpClient := feedsapi.NewClient("http://localhost:9600")
//XXX doesnt even work
//httpClient := feedsapi.NewClient("https://swarm-gateways.net")
// XXX: Post to multiple feeds?
//fmt.Println("signer Address: ", f.User.Hex())
request, err := httpClient.GetFeedRequest(query, "")
if err != nil {
fmt.Printf("Error retrieving feed status: %s", err.Error())
}
request.SetData(data)
if err = request.Sign(signer); err != nil {
fmt.Printf("Error signing feed update: %s", err.Error())
}
//fmt.Println("*** signed request", request)
// manifest, err := httpClient.CreateFeedWithManifest(request)
// if err != nil {
// fmt.Printf("Error getting manifest: %s", manifest)
// }
// fmt.Println("MANIFEST:", manifest)
// XXX: What do I want to do with feeds manifest?
// 567f611190b2758fa625b3be14b2b9becf6f0e8887015b7c40d6cbe0e5fa14aa
// Success: this works, also from 9601 Bob:
// curl 'http://localhost:9600/bzz-feed:/?user=0xBCa21d9c6031b1965a9e0233D9B905d2f10CA259&name=bob'
// XXX: Why do we need the second argument manifestAddressOrDomain?
// It's already baked into httpClient and query.
// Indeed:
// > You only need to provide either manifestAddressOrDomain or Query to QueryFeed(). Set to "" or nil respectively.
// response, err := httpClient.QueryFeed(query, "")
// if err != nil {
// fmt.Println("QueryFeed error", err)
// }
// buf := new(bytes.Buffer)
// buf.ReadFrom(response)
// feedStr := buf.String()
// fmt.Println("Feed result: ", feedStr)
// XXX Want to set level and time?
// POST /bzz-feed:/?topic=<TOPIC>&user=<USER>&level=<LEVEL>&time=<TIME>&signature=<SIGNATURE>
err = httpClient.UpdateFeed(request)
if err != nil {
fmt.Printf("Error updating feed: %s", err.Error())
}
}
type message struct {
Text string `json:"text"`
Parents []string `json:"parents"`
}
// check sign logic bits version etc
// XXX: Lame signature, should be may more compact
func runREPL(client *rpc.Client, signer *feed.GenericSigner, receiver string, topic string) {
fmt.Println("[I am Alice, and I am ready to send messages.]")
fmt.Printf("> ")
// Basic REPL functionality
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input := scanner.Text()
//sendMessage(input)
sendMessage(client, signer, receiver, topic, input)
fmt.Printf("> ")
}
if err := scanner.Err(); err != nil {
fmt.Println("Unable to read input", err)
os.Exit(1)
}
}
func mockPassiveREPL() {
// Poor Bob can only listen to messages, forever and ever
// Until one day...when he snaps and quits with ctrl-D
// Bob first shows loyalty, he never speaks, and then he exits
// Allowing Bob to speak means he'll be less likely to exit
// for { }
// fmt.Println("I am Bob or Charlie, and I can't speak right now.")
fmt.Printf("")
// Basic REPL functionality
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input := scanner.Text()
log.Debug("Ignoring input", input)
//fmt.Println("Echo", input)
//sendMessage(input)
//sendMessage(client, signer, receiver, topic, input)
//fmt.Printf("> ")
}
if err := scanner.Err(); err != nil {
fmt.Println("Unable to read input", err)
os.Exit(1)
}
// Ok maybe this is a bad idea?
}
// Get messages from feed
func pullMessages() {
// Success: this works, also from 9601 Bob. Replicate with Go API
// curl 'http://localhost:9600/bzz-feed:/?user=0xBCa21d9c6031b1965a9e0233D9B905d2f10CA259&name=bob'
// Querying with local node
// XXX Maybe this is a bad idea, what about gateway?
/// Cheating
httpClient := feedsapi.NewClient("http://localhost:9602") // XXX 9601
// local receiver bob
//httpClient := feedsapi.NewClient("http://localhost:9601")
// XXX: Probably not a great idea, but we need to make sure it is healthy etc
//httpClient := feedsapi.NewClient("https://swarm-gateways.net")
// Create a new feed with user and topic.
f := new(feed.Feed)
// Alice's dadress
f.User = common.HexToAddress("0xBCa21d9c6031b1965a9e0233D9B905d2f10CA259")
f.Topic, _ = feed.NewTopic("bob", nil)
query := feed.NewQueryLatest(f, lookup.NoClue)
// Look up feed results
// XXX: Why do we need the second argument manifestAddressOrDomain?
// It's already baked into httpClient and query.
response, err := httpClient.QueryFeed(query, "")
if err != nil {
fmt.Println("QueryFeed error", err)
}
buf := new(bytes.Buffer)
buf.ReadFrom(response)
feedStr := buf.String()
parsed := deserialize(feedStr)
handleMessage(parsed)
}
// XXX: This is so sloppy, passing privatekey around
func newService(bzzdir string, bzzport int, privKey *ecdsa.PrivateKey) func(ctx *node.ServiceContext) (node.Service, error) {
return func(ctx *node.ServiceContext) (node.Service, error) {
// Create bzzconfig
// TODO: Setup swarm port
// XXX: What's difference between Privkey and EnodeKey in Init?
bzzconfig := bzzapi.NewConfig()
bzzconfig.Path = bzzdir
bzzconfig.Port = fmt.Sprintf("%d", bzzport)
bzzconfig.Init(privKey, privKey)
return swarm.NewSwarm(bzzconfig, nil)
}
}
func addPeer(client *rpc.Client, enode string) {
// fmt.Println("addPeer", enode)
var res bool
err := client.Call(&res, "admin_addPeer", enode)
if err != nil {
fmt.Println("Lets also print unable to add peer here", err)
log.Crit("Unable to add peer", err)
}
// fmt.Println("addPeer res", res)
}
func run(port int, privateKey *ecdsa.PrivateKey) {
// New node
node, err := newNode(port)
if err != nil {
fmt.Fprintf(os.Stderr, "Node failure: %v\n", err)
os.Exit(1)
}
//fmt.Printf("Node: %v\n", node)
// New Swarm service
// XXX: Yuck privateKey
service := newService(node.InstanceDir(), port, privateKey)
// if err != nil {
// fmt.Fprint(os.Stderr, "Unable to start swarm service: %v\n", err)
// os.Exit(1)
// }
// fmt.Printf("Swarm service: %x\n", service)
// Register service
err = node.Register(service)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to register swarm service: %v\n", err)
os.Exit(1)
}
// Start the node
err = node.Start()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to start node: %v\n", err)
os.Exit(1)
}
// Get RPC Client
// ipcEndpoint
client, err := node.Attach()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to attach to client: %v\n", err)
}
// TODO: Refactor to have this addpeer workaround in separate function
// XXX: How to detect/derive this? Right now just grepping enode
enodeA := "enode://395a074c059143a68473bcf7edb3bae72bc930cfef5c92399401cedd76c493014d29e75ed1833fe45a2c0e04f0e9f9c64bf029c9c0fb646aa23690e945d70193@127.0.0.1:30400"
enodeB := "enode://f716c8cc7cc6674d8332ae1a3fb7f4776285095dc372c20a508e22e7d0a9c006b1626aab7b45802d99957b86bf1c0c14d9ba91df87528c735751e92dd96fa88f@127.0.0.1:30401"
// XXX: Broke discovery or something so not connected to many peers, adding some manually as a hack
// All have caps: ["bzz/8", "hive/8", "pss/2", "stream/8"] and came from standard swarm setup
//static1 := "enode://058f55a4bfe3ef7c3718ac035cd0bc5ce7646d8acc95930036145a0bcb337eb7769b015cb404201e63e48b261962728554e03c2ffca0a80e857f2c8ad1df02f4@52.232.7.187:30400?discport=0"
// static2 := "enode://a5d7168024c9992769cf380ffa559a64b4f39a29d468f579559863814eb0ae0ed689ac0871a3a2b4c78b03297485ec322d578281131ef5d5c09a4beb6200a97a@52.232.7.187:30442?discport=0"
// static3 := "enode://1ffa7651094867d6486ce3ef46d27a052c2cb968b618346c6df7040322c7efc3337547ba85d4cbba32e8b31c42c867202554735c06d4c664b9afada2ed0c4b3c@52.232.7.187:30412?discport=0"
// Ok, seems like there's an issue with hopCount being exceeded
// Additionally, unable to geth attach to b receiver specifically
// Let's try adding intermediate node
// All it does is...run? Hm.
// local third node - this has too much stuff in it
//enodeC := "enode://a87e2c53089904ec916e5b0c06524fa5dbfd69dc31f5446e7937cd1cdbcc61ed6173d17474814a778129ea0ce8b0fdfc4d4b4e9845a82f00358aa548975f6eae@127.0.0.1:30399"
enodeC := "enode://99c926eaa4276b79b255baaa8562ba124d7b7ecc81fc05e40679d4219ccbf01ec64f9068314cf20372e76d6dca4f02adf5db6570bc6774263fb1a2f7fc890bb6@127.0.0.1:30402"
// open internet
enodeD := "enode://99c926eaa4276b79b255baaa8562ba124d7b7ecc81fc05e40679d4219ccbf01ec64f9068314cf20372e76d6dca4f02adf5db6570bc6774263fb1a2f7fc890bb6@127.0.0.1:30402"
// // Consider conditionally adding peers, get who is self.
// if port == 9600 {
// addPeer(client, enodeB)
// } else if port == 9601 {
// addPeer(client, enodeA)
// }
// addPeer(client, enodeC)
addPeer(client, enodeA)
addPeer(client, enodeB)
addPeer(client, enodeC)
addPeer(client, enodeD)
// addPeer(client, static1)
// addPeer(client, static2)
// addPeer(client, static3)
// // XXX: Eh
// var res1 bool
// err = client.Call(&res1, "admin_addPeer", enodeA)
// if err != nil {
// log.Crit("Unable to add peer", err)
// }
// var res2 bool
// err = client.Call(&res2, "admin_addPeer", enodeB)
// if err != nil {
// log.Crit("Unable to add peer", err)
// }
//fmt.Println("*** added some peers probably ", res1, res2)
// TODO: admin get peers here?
// TODO: admin get peers ensure?
// XXX: More robust health check here
// Simpler, there should be a stdlib fn for waitHealthy anyway
time.Sleep(time.Second * 3)
// Hypothesis for why feed updates aren't spread - kademlia connectivity is bad
// Test it by measuring it
// HEALTH CHECK
//func (k *Kademlia) GetHealthInfo(pp *PeerPot) *Health {
//network.Kademlia
// TODO check how do
// XXX: Not readable
var nodeinfo p2p.NodeInfo
err = client.Call(&nodeinfo, "admin_nodeInfo")
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get node info: %v\n", err)
}
var baseaddr string
err = client.Call(&baseaddr, "pss_baseAddr")
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get base addr: %v\n", err)
}
//fmt.Println("baseAddr", baseaddr)
var pubkey string
err = client.Call(&pubkey, "pss_getPublicKey")
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to get pss public key: %v\n", err)
}
//fmt.Println("PublicKey", pubkey)
// XXX: Same with baseaddr
// 0xd5ac92dd8f593ea7aabf5cfdb35d4d29d87316ffe3ae081750054168e147eb42
bobBaseAddr := "0xb208b76c916d5eb84c118c0076b35828f0728a416537786f4515dd6608c4874d"
//fmt.Println("BobbaseAddr PSS", bobBaseAddr)
// XXX: This is separate from node generated key, so want to save this contacts or so
// f(alice/bob.key, 9600/9601), not confirmed if it is stable if port changes
// alice: 0x04c56131d8ded90e79b76b97f26e8fc032597886ff68ab6557f91c4bf1ae46eab94415c6df30bb6479cf40f811977bb2b237787f5b807d97191f7a994a558633e0
// bob: 0x04cbd6b75038f2d1e4e8e2754ffadecaaa8d2fdbbb29311dc82a8e1880fce4576f86e3d87ab360bcdde1aaf9a01a2cf232be95684a1152a735ccb4200495e4145c
var topic string
err = client.Call(&topic, "pss_stringToTopic", "bob")
bobPubKey := "0x04cbd6b75038f2d1e4e8e2754ffadecaaa8d2fdbbb29311dc82a8e1880fce4576f86e3d87ab360bcdde1aaf9a01a2cf232be95684a1152a735ccb4200495e4145c"
receiver := bobPubKey
//fmt.Println("BobPublicKey PSS", bobPubKey)
// XXX: I don't understand how baseAddr and public key interact
// XXX: I also don't understand why you need to register public key
err = client.Call(nil, "pss_setPeerPublicKey", bobPubKey, topic, bobBaseAddr)
msgC := make(chan pss.APIMsg)
// Nope, everyone gets to listen on that topic, also maybe it should be same?
// Does it matter for feeds?
// XXX: Let'ss try
sub, err := client.Subscribe(context.Background(), "pss", msgC, "receive", topic, false, false)
// Only Bob gets to listen
// if port == 9601 {
// sub, err := client.Subscribe(context.Background(), "pss", msgC, "receive", topic, false, false)
// if err != nil {
// fmt.Println("Error subscribing to topic", err)
// }
// defer sub.Unsubscribe()
// }
// XXX: Hack to make sure ready state
time.Sleep(time.Second * 3)
signer := feed.NewGenericSigner(privateKey)
// XXX: Hacky
// TODO: Replace with REPL-like functionality
if port == 9600 {
// NOTE: We assume here we are ready to actually send messages, so we REPL here
// XXX: Only running REPL for Alice Sender for now
runREPL(client, signer, receiver, topic)
} else if port == 9601 {
fmt.Println("[I am Bob, and I am ready to receive messages.]")
fmt.Println("[Syncing data...]")
pullMessages()
fmt.Println("[Synced up, listening for new mesages.]")
go listenForMessages(msgC)
mockPassiveREPL()
} else if port == 9602 {
fmt.Println("[I am Charlie, I'm just chilling here.]")
mockPassiveREPL()
} else {
fmt.Println("*** I don't know who you are")
os.Exit(1)
}
fmt.Printf("All operations successfully completed.\n")
// Teardown
sub.Unsubscribe()
client.Close()
node.Stop()
}
func init() {
// NOTE: taken from ethereum-samples
hs := log.StreamHandler(os.Stderr, log.TerminalFormat(true))
loglevel := log.LvlInfo
// TODO: Setup flag and -v option
// if *verbose {
// loglevel = log.LvlTrace
// }
loglevel = log.LvlDebug //trace
//loglevel = log.LvlTrace //trace
hf := log.LvlFilterHandler(loglevel, hs)
h := log.CallerFileHandler(hf)
log.Root().SetHandler(h)
}
// TODO: The swarm hash should match up with message ID
// How does this impact design?
// XXX: Ensure signature, also probably better with client as context but meh
// TODO: Message should include its parents
func sendMessage(client *rpc.Client, signer *feed.GenericSigner, receiver string, topic string, input string) {
// Keeping track of last parent, only one
msg := message{Text: input, Parents: []string{lastParent}}
payload := serialize(msg)
// 1. Send via PSS
err := client.Call(nil, "pss_sendAsym", receiver, topic, common.ToHex(payload))
if err != nil {
fmt.Println("Error sending message through RPC client", err)
os.Exit(1)
}
// 2. Upload to swarm to get hash for message dependency
// XXX: Might be more elegant way of doing this with manifests etc
// XXX: In so many places...
// TODO: Replace with 9600 once end to end
httpClient := feedsapi.NewClient("http://localhost:9602") // XXX 9600
hash, err := httpClient.UploadRaw(bytes.NewReader(payload), int64(len(payload)), false)
if err != nil {
fmt.Println("Unable to upload raw", err)
os.Exit(1)
}
// Now that we have hash, save this as lastParent
lastParent = hash
//fmt.Println("***last Parent", lastParent)
// 3. Also post to feed
// XXX: Currently hardcoded to plaintext name, could be hash of two pubkeys e.g.
// TODO: If any errors with this, show this
postToFeed(client, signer, receiver, topic, payload)
}
func serialize(msg message) []byte {
//msg := &message{Text: "hi", Parents: []string{"foo", "bar"}}
data, err := json.Marshal(msg)
if err != nil {
fmt.Println("Unable to marshal into JSON", err)
os.Exit(1)
}
return data
}
// func serialize2(msg message) string {
// //msg := &message{Text: "hi", Parents: []string{"foo", "bar"}}
// payload, err := json.Marshal(msg)
// if err != nil {
// fmt.Println("Unable to marshal into JSON", err)
// os.Exit(1)
// }
// //XXX shouldnt this be byte ?!
// strJSON := string(payload)
// //fmt.Println("json payload", strJSON)
// return strJSON
// }
func deserialize(strJSON string) message {
msg := message{}
json.Unmarshal([]byte(strJSON), &msg)
// fmt.Println(msg2)
return msg
}
func main() {
fmt.Println("[Hello PSS/Feeds!]")
fmt.Println("[Setting up node and connecting to the network...]")
// XXX Lets use already running node because why not
// TODO: Replace with 9600 once end to end
// XXX: Forgot did we use this somewhere?
//httpClient := feedsapi.NewClient("http://localhost:9602") // XXX 9600
// Ok, now we need to populate parent and fetch it
// To do this, we need a chunk that we upload
// Lets try basic swarm up and include that
// TODO: Then, integrate feed and update there too
// Cool, here ATM.
// Then we probably need some message deps
// TODO: As Alice we want to do a basic Feed post
// TODO: Then As Bob we want to do a basic Feed get pull, at startup or so
// Possibly ability to toggle this
// XXX: Actually how connected is this to the rest of the network? Who will store it?
// Create feed first
// TODO: Bad CLI design, use golang flags
// TODO: Pull this out to separate parseArgs function
args := os.Args[1:]
if len(args) == 1 {
if args[0] == "new" {
// TODO: Use keystore or something more sane
privateKey := getHexPrivateKey()
ioutil.WriteFile("new.key", []byte(privateKey), 0644)
log.Crit("Thanks for the fish, your private key is now insecurely stored in new.key", args)
} else {
log.Crit("Unknown argument, please use 'new' or two arguments (keyfile and port)", args)
}
} else if len(args) == 2 {
keyfile := args[0]
portArg := args[1]
// XXX error handling
privateKey := getPrivateKeyFromFile(keyfile)
port, err := strconv.Atoi(portArg)
if err != nil {
log.Crit("Unable to parse port argument", portArg)
}
// Start engines
run(port, privateKey)
} else {
log.Crit("Wrong number of arguments, should be one (new) or two (keyfile and port)", args)
}
}