arnaucube
arnaucube/go-dvotePublic Self-hosted repository
go-dvote/net/swarm/swarm.go
net/swarm/swarm.goRaw
  1package swarm
  2
  3import (
  4	"context"
  5	"crypto/ecdsa"
  6	"fmt"
  7	"os"
  8	"os/user"
  9	"strings"
 10	"time"
 11
 12	"github.com/ethereum/go-ethereum/common/hexutil"
 13	"github.com/ethereum/go-ethereum/node"
 14
 15	"github.com/ethereum/go-ethereum/crypto"
 16	"github.com/ethereum/go-ethereum/p2p"
 17
 18	"github.com/ethereum/go-ethereum/log"
 19	"github.com/ethereum/go-ethereum/p2p/enode"
 20	"github.com/ethereum/go-ethereum/swarm"
 21
 22	swarmapi "github.com/ethereum/go-ethereum/swarm/api"
 23	"github.com/ethereum/go-ethereum/swarm/network"
 24	"github.com/ethereum/go-ethereum/swarm/pss"
 25)
 26
 27const (
 28	// MaxPeers is the maximum number of p2p peer connections
 29	MaxPeers = 10
 30)
 31
 32// SwarmBootnodes list of bootnodes for the SWARM network
 33var SwarmBootnodes = []string{
 34	// EF Swarm Bootnode - AWS - eu-central-1
 35	"enode://4c113504601930bf2000c29bcd98d1716b6167749f58bad703bae338332fe93cc9d9204f08afb44100dc7bea479205f5d162df579f9a8f76f8b402d339709023@3.122.203.99:30301",
 36	// EF Swarm Bootnode - AWS - us-west-2
 37	"enode://89f2ede3371bff1ad9f2088f2012984e280287a4e2b68007c2a6ad994909c51886b4a8e9e2ecc97f9910aca538398e0a5804b0ee80a187fde1ba4f32626322ba@52.35.212.179:30301",
 38}
 39
 40func newNode(key *ecdsa.PrivateKey, port int, httpport int, wsport int,
 41	datadir string, modules ...string) (*node.Node, *node.Config, error) {
 42	if port == 0 {
 43		port = 30100
 44	}
 45	cfg := &node.DefaultConfig
 46	if key != nil {
 47		cfg.P2P.PrivateKey = key
 48	}
 49	cfg.P2P.MaxPeers = MaxPeers
 50	cfg.P2P.ListenAddr = fmt.Sprintf("0.0.0.0:%d", port)
 51	cfg.P2P.EnableMsgEvents = true
 52	cfg.P2P.NoDiscovery = false
 53	cfg.P2P.DiscoveryV5 = true
 54	cfg.IPCPath = datadir + "/node.ipc"
 55	cfg.DataDir = datadir
 56	if httpport > 0 {
 57		cfg.HTTPHost = node.DefaultHTTPHost
 58		cfg.HTTPPort = httpport
 59		cfg.HTTPCors = []string{"*"}
 60	}
 61	if wsport > 0 {
 62		cfg.WSHost = node.DefaultWSHost
 63		cfg.WSPort = wsport
 64		cfg.WSOrigins = []string{"*"}
 65		for i := 0; i < len(modules); i++ {
 66			cfg.WSModules = append(cfg.WSModules, modules[i])
 67		}
 68	}
 69	stack, err := node.New(cfg)
 70	if err != nil {
 71		return nil, nil, fmt.Errorf("ServiceNode create fail: %v", err)
 72	}
 73	return stack, cfg, nil
 74}
 75
 76func newSwarm(privkey *ecdsa.PrivateKey, datadir string, port int) (*swarm.Swarm, *swarmapi.Config, node.ServiceConstructor) {
 77	// create swarm service
 78	swarmCfg := swarmapi.NewConfig()
 79	swarmCfg.SyncEnabled = true
 80	swarmCfg.Port = fmt.Sprintf("%d", port)
 81	swarmCfg.Path = datadir
 82	swarmCfg.HiveParams.Discovery = true
 83	swarmCfg.Discovery = true
 84	swarmCfg.Pss.MsgTTL = time.Second * 10
 85	swarmCfg.Pss.CacheTTL = time.Second * 30
 86	swarmCfg.Pss.AllowRaw = true
 87	swarmCfg.Init(privkey)
 88	swarmNode, err := swarm.NewSwarm(swarmCfg, nil)
 89	if err != nil {
 90		log.Crit("cannot crate swarm node")
 91	}
 92	// register swarm service to the node
 93	var swarmService node.ServiceConstructor = func(ctx *node.ServiceContext) (node.Service, error) {
 94		return swarmNode, nil
 95	}
 96	return swarmNode, swarmCfg, swarmService
 97}
 98
 99type swarmPorts struct {
100	WebSockets int
101	HTTPRPC    int
102	Bzz        int
103	P2P        int
104}
105
106func NewSwarmPorts() *swarmPorts {
107	sp := new(swarmPorts)
108	sp.WebSockets = 8544
109	sp.HTTPRPC = 8543
110	sp.Bzz = 8542
111	sp.P2P = 31000
112	return sp
113}
114
115type pssSub struct {
116	Unregister func()
117	Delivery   (chan []byte)
118	Address    string
119}
120
121type SwarmNet struct {
122	Node       *node.Node
123	NodeConfig *node.Config
124	EnodeID    string
125	Datadir    string
126	Key        *ecdsa.PrivateKey
127	Pss        *pss.API
128	PssPubKey  string
129	PssAddr    pss.PssAddress
130	PssTopics  map[string]*pssSub
131	Hive       *network.Hive
132	Ports      *swarmPorts
133}
134
135func (sn *SwarmNet) SetLog(level string) error {
136	// ensure good log formats for terminal
137	// handle verbosity flag
138	loglevel, err := log.LvlFromString(level)
139	if err != nil {
140		return err
141	}
142	hs := log.StreamHandler(os.Stderr, log.TerminalFormat(true))
143	hf := log.LvlFilterHandler(loglevel, hs)
144	h := log.CallerFileHandler(hf)
145	log.Root().SetHandler(h)
146	return nil
147}
148
149func (sn *SwarmNet) PrintStats() {
150	// statistics thread
151	go func() {
152		for {
153			if sn.Node.Server() != nil && sn.Hive != nil {
154				addr := fmt.Sprintf("%x", sn.PssAddr)
155				var addrs [][]byte
156				addrs = append(addrs, []byte(addr))
157				peerCount := sn.Node.Server().PeerCount()
158				log.Info(fmt.Sprintf("PeerCount:%d NeighDepth:%d", peerCount, sn.Hive.NeighbourhoodDepth))
159			}
160			time.Sleep(time.Second * 5)
161		}
162	}()
163}
164
165func (sn *SwarmNet) SetDatadir(datadir string) {
166	sn.Datadir = datadir
167}
168
169func (sn *SwarmNet) SetKey(key *ecdsa.PrivateKey) {
170	sn.Key = key
171}
172
173func (sn *SwarmNet) Init() error {
174	var err error
175	if len(sn.Datadir) < 1 {
176		usr, err := user.Current()
177		if err != nil {
178			return err
179		}
180		sn.Datadir = usr.HomeDir + "/.dvote/swarm"
181		os.MkdirAll(sn.Datadir, 0755)
182	}
183
184	sn.SetLog("info")
185	sn.Ports = NewSwarmPorts()
186
187	// create node
188	sn.Node, sn.NodeConfig, err = newNode(sn.Key, sn.Ports.P2P,
189		sn.Ports.HTTPRPC, sn.Ports.WebSockets, sn.Datadir, "pss")
190	if err != nil {
191		return err
192	}
193	// set node key, if not set use the storage one or generate it
194	if sn.Key == nil {
195		sn.Key = sn.NodeConfig.NodeKey()
196	}
197
198	// create and register Swarm service
199	swarmNode, _, swarmHandler := newSwarm(sn.Key, sn.Datadir, sn.Ports.Bzz)
200	err = sn.Node.Register(swarmHandler)
201	if err != nil {
202		return fmt.Errorf("swarm register fail %v", err)
203	}
204
205	// start the node
206	sn.Node.Start()
207	for _, url := range SwarmBootnodes {
208		log.Info("Add bootnode " + url)
209		node, _ := enode.ParseV4(url)
210		sn.Node.Server().AddPeer(node)
211	}
212
213	// wait to connect to the p2p network
214	_, cancel := context.WithTimeout(context.Background(), time.Second)
215	defer cancel()
216	time.Sleep(time.Second * 5)
217
218	// Get the services API
219	for _, a := range swarmNode.APIs() {
220		switch a.Service.(type) {
221		case *network.Hive:
222			sn.Hive = a.Service.(*network.Hive)
223		case *pss.API:
224			sn.Pss = a.Service.(*pss.API)
225		}
226	}
227
228	// Create topics map
229	sn.PssTopics = make(map[string]*pssSub)
230
231	// Set some extra data
232	sn.EnodeID = sn.Node.Server().NodeInfo().Enode
233	sn.PssPubKey = hexutil.Encode(crypto.FromECDSAPub(sn.Pss.PublicKey()))
234	sn.PssAddr, err = sn.Pss.BaseAddr()
235	if err != nil {
236		return fmt.Errorf("pss API fail %v", err)
237	}
238
239	// Print some information
240	log.Info(fmt.Sprintf("My PSS pubkey is %s", sn.PssPubKey))
241	log.Info(fmt.Sprintf("My PSS address is %x", sn.PssAddr))
242
243	// Run statistics goroutine
244	sn.PrintStats()
245
246	return nil
247}
248
249func strTopic(topic string) pss.Topic {
250	return pss.BytesToTopic([]byte(topic))
251}
252
253func strSymKey(key string) []byte {
254	symKey := make([]byte, 32)
255	copy(symKey, []byte(key))
256	return symKey
257}
258
259func strAddress(addr string) pss.PssAddress {
260	var pssAddress pss.PssAddress
261	pssAddress = []byte(addr)
262	return pssAddress
263}
264
265func (sn *SwarmNet) PssSub(subType, key, topic, address string) error {
266	pssTopic := strTopic(topic)
267	pssAddress := strAddress(address)
268	switch subType {
269	case "sym":
270		_, err := sn.Pss.SetSymmetricKey(strSymKey(key), pssTopic, pssAddress, true)
271		if err != nil {
272			return err
273		}
274	}
275
276	sn.PssTopics[topic] = new(pssSub)
277	sn.PssTopics[topic].Address = address
278	sn.PssTopics[topic].Delivery = make(chan []byte)
279
280	var pssHandler pss.HandlerFunc = func(msg []byte, peer *p2p.Peer, asym bool, keyid string) error {
281		//log.Info("pss received", "msg", fmt.Sprintf("%s", msg), "keyid", fmt.Sprintf("%s", keyid))
282		sn.PssTopics[topic].Delivery <- msg
283		return nil
284	}
285	topicHandler := pss.NewHandler(pssHandler)
286	sn.PssTopics[topic].Unregister = sn.Pss.Register(&pssTopic, topicHandler)
287
288	log.Info(fmt.Sprintf("Subscribed to topic %s", pssTopic.String()))
289	return nil
290}
291
292func (sn *SwarmNet) PssPub(subType, key, topic, msg, address string) error {
293	var err error
294	dstAddr := strAddress(address)
295	dstTopic := strTopic(topic)
296	if subType == "sym" {
297		symKeyId, err := sn.Pss.SetSymmetricKey(strSymKey(key), dstTopic, dstAddr, false)
298		if err != nil {
299			return err
300		}
301		err = sn.Pss.SendSym(symKeyId, strTopic(topic), hexutil.Bytes(msg))
302	}
303	if subType == "raw" {
304		err = sn.Pss.SendRaw(hexutil.Bytes(dstAddr), dstTopic, hexutil.Bytes(msg))
305	}
306	if subType == "asym" {
307		if hasHexPrefix := strings.HasPrefix(key, "0x"); !hasHexPrefix {
308			key = "0x" + key
309		}
310		topics, addresses, err := sn.Pss.GetPublickeyPeers(key)
311		if err != nil {
312			return err
313		}
314		topicFound := false
315		for i, t := range topics {
316			if dstTopic == t && fmt.Sprintf("%x", addresses[i]) == fmt.Sprintf("%x", dstAddr) {
317				topicFound = true
318				break
319			}
320		}
321		if !topicFound {
322			pubKeyBytes, err := hexutil.Decode(key)
323			if err != nil {
324				return err
325			}
326			err = sn.Pss.SetPeerPublicKey(pubKeyBytes, dstTopic, dstAddr)
327			if err != nil {
328				return err
329			}
330		}
331
332		err = sn.Pss.SendAsym(key, dstTopic, hexutil.Bytes(msg))
333	}
334	return err
335}
336
337func (sn *SwarmNet) Test() error {
338	sn.PssSub("sym", "vocdoni", "vocdoni_test", "")
339
340	go func() {
341		for {
342			msg := <-sn.PssTopics["vocdoni_test"].Delivery
343			fmt.Printf("Pss received: %s\n", msg)
344		}
345	}()
346
347	hostname, _ := os.Hostname()
348	for {
349		err := sn.PssPub("sym", "vocdoni", "vocdoni_test", fmt.Sprintf("Hello world from %s", hostname), "")
350		log.Info("pss sent", "err", err)
351		time.Sleep(10 * time.Second)
352	}
353}