forked from RedBird96/b7s
-
Notifications
You must be signed in to change notification settings - Fork 1
/
host.go
184 lines (140 loc) · 4.7 KB
/
host.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package host
import (
"errors"
"fmt"
"os"
"github.com/asaskevich/govalidator"
"github.com/rs/zerolog"
"github.com/libp2p/go-libp2p"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/host"
ma "github.com/multiformats/go-multiaddr"
)
// Host represents a new libp2p host.
type Host struct {
host.Host
log zerolog.Logger
cfg Config
pubsub *pubsub.PubSub
}
// New creates a new Host.
func New(log zerolog.Logger, address string, port uint, options ...func(*Config)) (*Host, error) {
cfg := defaultConfig
for _, option := range options {
option(&cfg)
}
hostAddress := fmt.Sprintf("/ip4/%v/tcp/%v", address, port)
addresses := []string{
hostAddress,
}
if cfg.Websocket {
// If the TCP and websocket port are explicitly chosen and set to the same value, one of the two listens will silently fail.
if port == cfg.WebsocketPort && cfg.WebsocketPort != 0 {
return nil, fmt.Errorf("TCP and websocket ports cannot be the same (TCP: %v, Websocket: %v)", port, cfg.WebsocketPort)
}
wsAddr := fmt.Sprintf("/ip4/%v/tcp/%v/ws", address, cfg.WebsocketPort)
addresses = append(addresses, wsAddr)
}
opts := []libp2p.Option{
libp2p.ListenAddrStrings(addresses...),
libp2p.DefaultTransports,
libp2p.DefaultMuxers,
libp2p.DefaultSecurity,
libp2p.NATPortMap(),
}
// Read private key, if provided.
if cfg.PrivateKey != "" {
key, err := readPrivateKey(cfg.PrivateKey)
if err != nil {
return nil, fmt.Errorf("could not read private key: %w", err)
}
opts = append(opts, libp2p.Identity(key))
}
if cfg.DialBackAddress != "" && cfg.DialBackPort != 0 {
protocol, dialbackAddress, err := determineAddressProtocol(cfg.DialBackAddress)
if err != nil {
return nil, fmt.Errorf("could not parse dialback multiaddress (address: %s): %w", cfg.DialBackAddress, err)
}
externalAddr := fmt.Sprintf("/%v/%v/tcp/%v", protocol, dialbackAddress, cfg.DialBackPort)
extAddresses := []string{
externalAddr,
}
if cfg.Websocket && cfg.DialBackWebsocketPort != 0 {
if cfg.DialBackWebsocketPort == cfg.DialBackPort {
return nil, fmt.Errorf("TCP and websocket dialback ports cannot be the same (TCP: %v, Websocket: %v)", cfg.DialBackPort, cfg.DialBackWebsocketPort)
}
externalWsAddr := fmt.Sprintf("/%v/%v/tcp/%v/ws", protocol, dialbackAddress, cfg.WebsocketPort)
extAddresses = append(extAddresses, externalWsAddr)
}
// Create list of multiaddrs with the external IP and port.
var externalAddrs []ma.Multiaddr
for _, addr := range extAddresses {
maddr, err := ma.NewMultiaddr(addr)
if err != nil {
return nil, fmt.Errorf("could not create external multiaddress: %w", err)
}
externalAddrs = append(externalAddrs, maddr)
}
addrFactory := libp2p.AddrsFactory(func(addrs []ma.Multiaddr) []ma.Multiaddr {
// Return only the external multiaddrs.
return externalAddrs
})
opts = append(opts, addrFactory)
}
// Create libp2p host.
h, err := libp2p.New(opts...)
if err != nil {
return nil, fmt.Errorf("could not create libp2p host: %w", err)
}
host := Host{
log: log.With().Str("component", "host").Logger(),
cfg: cfg,
}
host.Host = h
return &host, nil
}
// PrivateKey returns the private key of the libp2p host.
func (h *Host) PrivateKey() crypto.PrivKey {
return h.Peerstore().PrivKey(h.ID())
}
// PublicKey returns the public key of the libp2p host.
func (h *Host) PublicKey() crypto.PubKey {
return h.Peerstore().PubKey(h.ID())
}
// Addresses returns the list of p2p addresses of the host.
func (h *Host) Addresses() []string {
addrs := h.Addrs()
out := make([]string, 0, len(addrs))
hostID := h.ID()
for _, addr := range addrs {
addr := fmt.Sprintf("%s/p2p/%s", addr.String(), hostID.String())
out = append(out, addr)
}
return out
}
func readPrivateKey(filepath string) (crypto.PrivKey, error) {
payload, err := os.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("could not read file: %w", err)
}
key, err := crypto.UnmarshalPrivateKey(payload)
if err != nil {
return nil, fmt.Errorf("could not unmarshal private key: %w", err)
}
return key, nil
}
// determineAddressProtocol parses the provided address and tries to determine its type. We typically expect either a IPv4, IPv6 or a hostname.
// At times it's a bit tricky to determine the address type in Go and a lot of parsers end up guessing when dealing with some more exotic variants.
func determineAddressProtocol(address string) (string, string, error) {
if govalidator.IsIPv4(address) {
return "ip4", address, nil
}
if govalidator.IsIPv6(address) {
return "ip6", address, nil
}
if govalidator.IsDNSName(address) {
return "dns", address, nil
}
return "", "", errors.New("could not parse address")
}