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() {
136	// ensure good log formats for terminal
137	// handle verbosity flag
138	hs := log.StreamHandler(os.Stderr, log.TerminalFormat(true))
139	loglevel := log.LvlInfo
140	hf := log.LvlFilterHandler(loglevel, hs)
141	h := log.CallerFileHandler(hf)
142	log.Root().SetHandler(h)
143}
144
145func (sn *SwarmNet) PrintStats() {
146	// statistics thread
147	go func() {
148		for {
149			if sn.Node.Server() != nil && sn.Hive != nil {
150				addr := fmt.Sprintf("%x", sn.PssAddr)
151				var addrs [][]byte
152				addrs = append(addrs, []byte(addr))
153				peerCount := sn.Node.Server().PeerCount()
154				log.Info(fmt.Sprintf("PeerCount:%d NeighDepth:%d", peerCount, sn.Hive.NeighbourhoodDepth))
155			}
156			time.Sleep(time.Second * 5)
157		}
158	}()
159}
160
161func (sn *SwarmNet) SetDatadir(datadir string) {
162	sn.Datadir = datadir
163}
164
165func (sn *SwarmNet) SetKey(key *ecdsa.PrivateKey) {
166	sn.Key = key
167}
168
169func (sn *SwarmNet) Init() error {
170	var err error
171	if len(sn.Datadir) < 1 {
172		usr, err := user.Current()
173		if err != nil {
174			return err
175		}
176		sn.Datadir = usr.HomeDir + "/.dvote/swarm"
177		os.MkdirAll(sn.Datadir, 0755)
178	}
179
180	sn.SetLog()
181	sn.Ports = NewSwarmPorts()
182
183	// create node
184	sn.Node, sn.NodeConfig, err = newNode(sn.Key, sn.Ports.P2P,
185		sn.Ports.HTTPRPC, sn.Ports.WebSockets, sn.Datadir, "pss")
186	if err != nil {
187		return err
188	}
189	// set node key, if not set use the storage one or generate it
190	if sn.Key == nil {
191		sn.Key = sn.NodeConfig.NodeKey()
192	}
193
194	// create and register Swarm service
195	swarmNode, _, swarmHandler := newSwarm(sn.Key, sn.Datadir, sn.Ports.Bzz)
196	err = sn.Node.Register(swarmHandler)
197	if err != nil {
198		return fmt.Errorf("swarm register fail %v", err)
199	}
200
201	// start the node
202	sn.Node.Start()
203	for _, url := range SwarmBootnodes {
204		log.Info("Add bootnode " + url)
205		node, _ := enode.ParseV4(url)
206		sn.Node.Server().AddPeer(node)
207	}
208
209	// wait to connect to the p2p network
210	_, cancel := context.WithTimeout(context.Background(), time.Second)
211	defer cancel()
212	time.Sleep(time.Second * 5)
213
214	// Get the services API
215	for _, a := range swarmNode.APIs() {
216		switch a.Service.(type) {
217		case *network.Hive:
218			sn.Hive = a.Service.(*network.Hive)
219		case *pss.API:
220			sn.Pss = a.Service.(*pss.API)
221		}
222	}
223
224	// Create topics map
225	sn.PssTopics = make(map[string]*pssSub)
226
227	// Set some extra data
228	sn.EnodeID = sn.Node.Server().NodeInfo().Enode
229	sn.PssPubKey = hexutil.Encode(crypto.FromECDSAPub(sn.Pss.PublicKey()))
230	sn.PssAddr, err = sn.Pss.BaseAddr()
231	if err != nil {
232		return fmt.Errorf("pss API fail %v", err)
233	}
234
235	// Print some information
236	log.Info(fmt.Sprintf("My PSS pubkey is %s", sn.PssPubKey))
237	log.Info(fmt.Sprintf("My PSS address is %x", sn.PssAddr))
238
239	// Run statistics goroutine
240	sn.PrintStats()
241
242	return nil
243}
244
245func strTopic(topic string) pss.Topic {
246	return pss.BytesToTopic([]byte(topic))
247}
248
249func strSymKey(key string) []byte {
250	symKey := make([]byte, 32)
251	copy(symKey, []byte(key))
252	return symKey
253}
254
255func strAddress(addr string) pss.PssAddress {
256	var pssAddress pss.PssAddress
257	pssAddress = []byte(addr)
258	return pssAddress
259}
260
261func (sn *SwarmNet) PssSub(subType, key, topic, address string) error {
262	pssTopic := strTopic(topic)
263	pssAddress := strAddress(address)
264	switch subType {
265	case "sym":
266		_, err := sn.Pss.SetSymmetricKey(strSymKey(key), pssTopic, pssAddress, true)
267		if err != nil {
268			return err
269		}
270	}
271
272	sn.PssTopics[topic] = new(pssSub)
273	sn.PssTopics[topic].Address = address
274	sn.PssTopics[topic].Delivery = make(chan []byte)
275
276	var pssHandler pss.HandlerFunc = func(msg []byte, peer *p2p.Peer, asym bool, keyid string) error {
277		//log.Info("pss received", "msg", fmt.Sprintf("%s", msg), "keyid", fmt.Sprintf("%s", keyid))
278		sn.PssTopics[topic].Delivery <- msg
279		return nil
280	}
281	topicHandler := pss.NewHandler(pssHandler)
282	sn.PssTopics[topic].Unregister = sn.Pss.Register(&pssTopic, topicHandler)
283
284	log.Info(fmt.Sprintf("Subscribed to topic %s", pssTopic.String()))
285	return nil
286}
287
288func (sn *SwarmNet) PssPub(subType, key, topic, msg, address string) error {
289	var err error
290	dstAddr := strAddress(address)
291	dstTopic := strTopic(topic)
292	if subType == "sym" {
293		symKeyId, err := sn.Pss.SetSymmetricKey(strSymKey(key), dstTopic, dstAddr, false)
294		if err != nil {
295			return err
296		}
297		err = sn.Pss.SendSym(symKeyId, strTopic(topic), hexutil.Bytes(msg))
298	}
299	if subType == "raw" {
300		err = sn.Pss.SendRaw(hexutil.Bytes(dstAddr), dstTopic, hexutil.Bytes(msg))
301	}
302	if subType == "asym" {
303		if hasHexPrefix := strings.HasPrefix(key, "0x"); !hasHexPrefix {
304			key = "0x" + key
305		}
306		topics, addresses, err := sn.Pss.GetPublickeyPeers(key)
307		if err != nil {
308			return err
309		}
310		topicFound := false
311		for i, t := range topics {
312			if dstTopic == t && fmt.Sprintf("%x", addresses[i]) == fmt.Sprintf("%x", dstAddr) {
313				topicFound = true
314				break
315			}
316		}
317		if !topicFound {
318			pubKeyBytes, err := hexutil.Decode(key)
319			if err != nil {
320				return err
321			}
322			err = sn.Pss.SetPeerPublicKey(pubKeyBytes, dstTopic, dstAddr)
323			if err != nil {
324				return err
325			}
326		}
327
328		err = sn.Pss.SendAsym(key, dstTopic, hexutil.Bytes(msg))
329	}
330	return err
331}
332
333func (sn *SwarmNet) Test() error {
334	sn.PssSub("sym", "vocdoni", "vocdoni_test", "")
335
336	go func() {
337		for {
338			msg := <-sn.PssTopics["vocdoni_test"].Delivery
339			fmt.Printf("Pss received: %s\n", msg)
340		}
341	}()
342
343	hostname, _ := os.Hostname()
344	for {
345		err := sn.PssPub("sym", "vocdoni", "vocdoni_test", fmt.Sprintf("Hello world from %s", hostname), "")
346		log.Info("pss sent", "err", err)
347		time.Sleep(10 * time.Second)
348	}
349}