1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package peer
7
8import (
9	"sync"
10
11	"github.com/bitmark-inc/bitmarkd/announce"
12	"github.com/bitmark-inc/bitmarkd/background"
13	"github.com/bitmark-inc/bitmarkd/fault"
14	"github.com/bitmark-inc/bitmarkd/peer/upstream"
15	"github.com/bitmark-inc/bitmarkd/util"
16	"github.com/bitmark-inc/bitmarkd/zmqutil"
17	"github.com/bitmark-inc/logger"
18)
19
20// Connection - hardwired connections
21// this is read from the configuration file
22type Connection struct {
23	PublicKey string `gluamapper:"public_key" json:"public_key"`
24	Address   string `gluamapper:"address" json:"address"`
25}
26
27// Configuration - a block of configuration data
28// this is read from the configuration file
29type Configuration struct {
30	DynamicConnections bool         `gluamapper:"dynamic_connections" json:"dynamic_connections"`
31	PreferIPv6         bool         `gluamapper:"prefer_ipv6" json:"prefer_ipv6"`
32	Listen             []string     `gluamapper:"listen" json:"listen"`
33	Announce           []string     `gluamapper:"announce" json:"announce"`
34	PrivateKey         string       `gluamapper:"private_key" json:"private_key"`
35	PublicKey          string       `gluamapper:"public_key" json:"public_key"`
36	Connect            []Connection `gluamapper:"connect" json:"connect,omitempty"`
37}
38
39// globals for background process
40type peerData struct {
41	sync.RWMutex // to allow locking
42
43	log *logger.L // logger
44
45	lstn listener  // for RPC responses
46	conn connector // for RPC requests
47
48	connectorClients []upstream.Upstream
49
50	publicKey []byte
51
52	clientCount int
53	blockHeight uint64
54
55	// for background
56	background *background.T
57
58	// set once during initialise
59	initialised bool
60}
61
62// global data
63var globalData peerData
64
65// Initialise - setup peer background processes
66func Initialise(configuration *Configuration, version string, fastsync bool) error {
67
68	globalData.Lock()
69	defer globalData.Unlock()
70
71	// no need to start if already started
72	if globalData.initialised {
73		return fault.AlreadyInitialised
74	}
75
76	globalData.log = logger.New("peer")
77	globalData.log.Info("starting…")
78
79	// read the keys
80	privateKey, err := zmqutil.ReadPrivateKey(configuration.PrivateKey)
81	if nil != err {
82		globalData.log.Errorf("read private key file: %q  error: %s", configuration.PrivateKey, err)
83		return err
84	}
85	publicKey, err := zmqutil.ReadPublicKey(configuration.PublicKey)
86	if nil != err {
87		globalData.log.Errorf("read public key file: %q  error: %s", configuration.PublicKey, err)
88		return err
89	}
90	globalData.log.Tracef("peer private key: %q", privateKey)
91	globalData.log.Tracef("peer public key:  %q", publicKey)
92
93	globalData.publicKey = publicKey
94
95	// set up announcer before any connections
96	err = setAnnounce(configuration, publicKey)
97	if nil != err {
98		return err
99	}
100
101	if err := globalData.lstn.initialise(privateKey, publicKey, configuration.Listen, version); nil != err {
102		return err
103	}
104	if err := globalData.conn.initialise(privateKey, publicKey, configuration.Connect, configuration.DynamicConnections, configuration.PreferIPv6, fastsync); nil != err {
105		return err
106	}
107
108	// all data initialised
109	globalData.initialised = true
110
111	// start background processes
112	globalData.log.Info("start background…")
113
114	processes := background.Processes{
115		// &globalData.brdc,
116		&globalData.lstn,
117		&globalData.conn,
118	}
119
120	globalData.background = background.Start(processes, globalData.log)
121
122	return nil
123}
124
125// configure announce so that minimum data will be present for
126// connection to neighbours
127func setAnnounce(configuration *Configuration, publicKey []byte) error {
128
129	l := make([]byte, 0, 100) // ***** FIX THIS: need a better default size
130
131process_listen:
132	for i, address := range configuration.Announce {
133		if "" == address {
134			continue process_listen
135		}
136		c, err := util.NewConnection(address)
137		if nil != err {
138			globalData.log.Errorf("announce listen[%d]=%q  error: %s", i, address, err)
139			return err
140		}
141		l = append(l, c.Pack()...)
142	}
143	if err := announce.SetSelf(publicKey, l); nil != err {
144		globalData.log.Errorf("announce.SetPeer error: %s", err)
145		return err
146	}
147	return nil
148}
149
150// Finalise - stop all background tasks
151func Finalise() error {
152
153	if !globalData.initialised {
154		return fault.NotInitialised
155	}
156
157	globalData.log.Info("shutting down…")
158	globalData.log.Flush()
159
160	// stop background
161	globalData.background.Stop()
162
163	// finally...
164	globalData.initialised = false
165
166	globalData.log.Info("finished")
167	globalData.log.Flush()
168
169	return nil
170}
171
172// PublicKey - return public key
173func PublicKey() []byte {
174	return globalData.publicKey
175}
176
177// GetCounts - return connection counts:
178//   incoming - total peers connecting to all listeners
179//   outgoing - total outgoing connections
180func GetCounts() (uint64, uint64) {
181	return globalData.lstn.connections, uint64(globalData.clientCount)
182}
183
184// BlockHeight - return global block height
185func BlockHeight() uint64 {
186	return globalData.blockHeight
187}
188