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