github.com/ethereum-optimism/optimism@v1.7.2/op-node/p2p/cli/load_config.go (about) 1 package cli 2 3 import ( 4 "crypto/rand" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "os" 11 "strings" 12 13 "github.com/ethereum-optimism/optimism/op-node/rollup" 14 ds "github.com/ipfs/go-datastore" 15 "github.com/ipfs/go-datastore/sync" 16 leveldb "github.com/ipfs/go-ds-leveldb" 17 "github.com/libp2p/go-libp2p/core/crypto" 18 "github.com/multiformats/go-multiaddr" 19 20 "github.com/ethereum-optimism/optimism/op-node/flags" 21 "github.com/ethereum-optimism/optimism/op-node/p2p" 22 23 "github.com/urfave/cli/v2" 24 25 "github.com/ethereum/go-ethereum/p2p/enode" 26 "github.com/ethereum/go-ethereum/p2p/netutil" 27 ) 28 29 func NewConfig(ctx *cli.Context, rollupCfg *rollup.Config) (*p2p.Config, error) { 30 conf := &p2p.Config{} 31 32 if ctx.Bool(flags.DisableP2PName) { 33 conf.DisableP2P = true 34 return conf, nil 35 } 36 37 p, err := loadNetworkPrivKey(ctx) 38 if err != nil { 39 return nil, fmt.Errorf("failed to load p2p priv key: %w", err) 40 } 41 conf.Priv = p 42 43 if err := loadListenOpts(conf, ctx); err != nil { 44 return nil, fmt.Errorf("failed to load p2p listen options: %w", err) 45 } 46 47 if err := loadDiscoveryOpts(conf, ctx); err != nil { 48 return nil, fmt.Errorf("failed to load p2p discovery options: %w", err) 49 } 50 51 if err := loadLibp2pOpts(conf, ctx); err != nil { 52 return nil, fmt.Errorf("failed to load p2p options: %w", err) 53 } 54 55 if err := loadGossipOptions(conf, ctx); err != nil { 56 return nil, fmt.Errorf("failed to load p2p gossip options: %w", err) 57 } 58 59 if err := loadScoringParams(conf, ctx, rollupCfg); err != nil { 60 return nil, fmt.Errorf("failed to load p2p peer scoring options: %w", err) 61 } 62 63 if err := loadBanningOptions(conf, ctx); err != nil { 64 return nil, fmt.Errorf("failed to load banning option: %w", err) 65 } 66 67 conf.EnableReqRespSync = ctx.Bool(flags.SyncReqRespName) 68 conf.EnablePingService = ctx.Bool(flags.P2PPingName) 69 70 return conf, nil 71 } 72 73 func validatePort(p uint) (uint16, error) { 74 if p == 0 { 75 return 0, nil 76 } 77 if p >= (1 << 16) { 78 return 0, fmt.Errorf("port out of range: %d", p) 79 } 80 if p < 1024 { 81 return 0, fmt.Errorf("port is reserved for system: %d", p) 82 } 83 return uint16(p), nil 84 } 85 86 // loadScoringParams loads the peer scoring options from the CLI context. 87 func loadScoringParams(conf *p2p.Config, ctx *cli.Context, rollupCfg *rollup.Config) error { 88 scoringLevel := ctx.String(flags.ScoringName) 89 // Check old names for backwards compatibility 90 if scoringLevel == "" { 91 scoringLevel = ctx.String(flags.PeerScoringName) 92 } 93 if scoringLevel == "" { 94 scoringLevel = ctx.String(flags.TopicScoringName) 95 } 96 if scoringLevel != "" { 97 params, err := p2p.GetScoringParams(scoringLevel, rollupCfg) 98 if err != nil { 99 return err 100 } 101 conf.ScoringParams = params 102 } 103 104 return nil 105 } 106 107 // loadBanningOptions loads whether or not to ban peers from the CLI context. 108 func loadBanningOptions(conf *p2p.Config, ctx *cli.Context) error { 109 conf.BanningEnabled = ctx.Bool(flags.BanningName) 110 conf.BanningThreshold = ctx.Float64(flags.BanningThresholdName) 111 conf.BanningDuration = ctx.Duration(flags.BanningDurationName) 112 return nil 113 } 114 115 func loadListenOpts(conf *p2p.Config, ctx *cli.Context) error { 116 listenIP := ctx.String(flags.ListenIPName) 117 if listenIP != "" { // optional 118 conf.ListenIP = net.ParseIP(listenIP) 119 if conf.ListenIP == nil { 120 return fmt.Errorf("failed to parse IP %q", listenIP) 121 } 122 } 123 var err error 124 conf.ListenTCPPort, err = validatePort(ctx.Uint(flags.ListenTCPPortName)) 125 if err != nil { 126 return fmt.Errorf("bad listen TCP port: %w", err) 127 } 128 conf.ListenUDPPort, err = validatePort(ctx.Uint(flags.ListenUDPPortName)) 129 if err != nil { 130 return fmt.Errorf("bad listen UDP port: %w", err) 131 } 132 return nil 133 } 134 135 func loadDiscoveryOpts(conf *p2p.Config, ctx *cli.Context) error { 136 if ctx.Bool(flags.NoDiscoveryName) { 137 conf.NoDiscovery = true 138 } 139 140 var err error 141 conf.AdvertiseTCPPort, err = validatePort(ctx.Uint(flags.AdvertiseTCPPortName)) 142 if err != nil { 143 return fmt.Errorf("bad advertised TCP port: %w", err) 144 } 145 conf.AdvertiseUDPPort, err = validatePort(ctx.Uint(flags.AdvertiseUDPPortName)) 146 if err != nil { 147 return fmt.Errorf("bad advertised UDP port: %w", err) 148 } 149 adIP := ctx.String(flags.AdvertiseIPName) 150 if adIP != "" { // optional 151 ips, err := net.LookupIP(adIP) 152 if err != nil { 153 return fmt.Errorf("failed to lookup IP of %q to advertise in ENR: %w", adIP, err) 154 } 155 // Find the first v4 IP it resolves to 156 for _, ip := range ips { 157 if ipv4 := ip.To4(); ipv4 != nil { 158 conf.AdvertiseIP = ipv4 159 break 160 } 161 } 162 if conf.AdvertiseIP == nil { 163 return fmt.Errorf("failed to parse IP %q", adIP) 164 } 165 } 166 167 dbPath := ctx.String(flags.DiscoveryPathName) 168 if dbPath == "" { 169 dbPath = "opnode_discovery_db" 170 } 171 if dbPath == "memory" { 172 dbPath = "" 173 } 174 conf.DiscoveryDB, err = enode.OpenDB(dbPath) 175 if err != nil { 176 return fmt.Errorf("failed to open discovery db: %w", err) 177 } 178 179 bootnodes := make([]*enode.Node, 0) 180 records := strings.Split(ctx.String(flags.BootnodesName), ",") 181 for i, recordB64 := range records { 182 recordB64 = strings.TrimSpace(recordB64) 183 if recordB64 == "" { // ignore empty records 184 continue 185 } 186 nodeRecord, err := enode.Parse(enode.ValidSchemes, recordB64) 187 if err != nil { 188 return fmt.Errorf("bootnode record %d (of %d) is invalid: %q err: %w", i, len(records), recordB64, err) 189 } 190 bootnodes = append(bootnodes, nodeRecord) 191 } 192 if len(bootnodes) > 0 { 193 conf.Bootnodes = bootnodes 194 } else { 195 conf.Bootnodes = p2p.DefaultBootnodes 196 } 197 198 if ctx.IsSet(flags.NetRestrictName) { 199 netRestrict, err := netutil.ParseNetlist(ctx.String(flags.NetRestrictName)) 200 if err != nil { 201 return fmt.Errorf("failed to parse net list: %w", err) 202 } 203 conf.NetRestrict = netRestrict 204 } 205 206 return nil 207 } 208 209 func loadLibp2pOpts(conf *p2p.Config, ctx *cli.Context) error { 210 addrs := strings.Split(ctx.String(flags.StaticPeersName), ",") 211 for i, addr := range addrs { 212 addr = strings.TrimSpace(addr) 213 if addr == "" { 214 continue // skip empty multi addrs 215 } 216 a, err := multiaddr.NewMultiaddr(addr) 217 if err != nil { 218 return fmt.Errorf("failed to parse multi addr of static peer %d (out of %d): %q err: %w", i, len(addrs), addr, err) 219 } 220 conf.StaticPeers = append(conf.StaticPeers, a) 221 } 222 223 for _, v := range strings.Split(ctx.String(flags.HostMuxName), ",") { 224 v = strings.ToLower(strings.TrimSpace(v)) 225 switch v { 226 case "yamux": 227 conf.HostMux = append(conf.HostMux, p2p.YamuxC()) 228 case "mplex": 229 conf.HostMux = append(conf.HostMux, p2p.MplexC()) 230 default: 231 return fmt.Errorf("could not recognize mux %s", v) 232 } 233 } 234 235 secArr := strings.Split(ctx.String(flags.HostSecurityName), ",") 236 for _, v := range secArr { 237 v = strings.ToLower(strings.TrimSpace(v)) 238 switch v { 239 case "none": // no security, for debugging etc. 240 if len(conf.HostSecurity) > 0 || len(secArr) > 1 { 241 return errors.New("cannot mix secure transport protocols with no-security") 242 } 243 conf.NoTransportSecurity = true 244 case "noise": 245 conf.HostSecurity = append(conf.HostSecurity, p2p.NoiseC()) 246 case "tls": 247 conf.HostSecurity = append(conf.HostSecurity, p2p.TlsC()) 248 default: 249 return fmt.Errorf("could not recognize security %s", v) 250 } 251 } 252 253 conf.PeersLo = ctx.Uint(flags.PeersLoName) 254 conf.PeersHi = ctx.Uint(flags.PeersHiName) 255 conf.PeersGrace = ctx.Duration(flags.PeersGraceName) 256 conf.NAT = ctx.Bool(flags.NATName) 257 conf.UserAgent = ctx.String(flags.UserAgentName) 258 conf.TimeoutNegotiation = ctx.Duration(flags.TimeoutNegotiationName) 259 conf.TimeoutAccept = ctx.Duration(flags.TimeoutAcceptName) 260 conf.TimeoutDial = ctx.Duration(flags.TimeoutDialName) 261 262 peerstorePath := ctx.String(flags.PeerstorePathName) 263 if peerstorePath == "" { 264 return errors.New("peerstore path must be specified, use 'memory' to explicitly not persist peer records") 265 } 266 267 var err error 268 var store ds.Batching 269 if peerstorePath == "memory" { 270 store = sync.MutexWrap(ds.NewMapDatastore()) 271 } else { 272 store, err = leveldb.NewDatastore(peerstorePath, nil) // default leveldb options are fine 273 if err != nil { 274 return fmt.Errorf("failed to open leveldb db for peerstore: %w", err) 275 } 276 } 277 conf.Store = store 278 279 return nil 280 } 281 282 func loadNetworkPrivKey(ctx *cli.Context) (*crypto.Secp256k1PrivateKey, error) { 283 raw := ctx.String(flags.P2PPrivRawName) 284 if raw != "" { 285 return parsePriv(raw) 286 } 287 keyPath := ctx.String(flags.P2PPrivPathName) 288 if keyPath == "" { 289 return nil, errors.New("no p2p private key path specified, cannot auto-generate key without path") 290 } 291 f, err := os.OpenFile(keyPath, os.O_RDONLY, 0600) 292 if os.IsNotExist(err) { 293 p, _, err := crypto.GenerateSecp256k1Key(rand.Reader) 294 if err != nil { 295 return nil, fmt.Errorf("failed to generate new p2p priv key: %w", err) 296 } 297 b, err := p.Raw() 298 if err != nil { 299 return nil, fmt.Errorf("failed to encode new p2p priv key: %w", err) 300 } 301 f, err := os.OpenFile(keyPath, os.O_CREATE|os.O_WRONLY, 0600) 302 if err != nil { 303 return nil, fmt.Errorf("failed to store new p2p priv key: %w", err) 304 } 305 defer f.Close() 306 if _, err := f.WriteString(hex.EncodeToString(b)); err != nil { 307 return nil, fmt.Errorf("failed to write new p2p priv key: %w", err) 308 } 309 return (p).(*crypto.Secp256k1PrivateKey), nil 310 } else { 311 defer f.Close() 312 data, err := io.ReadAll(f) 313 if err != nil { 314 return nil, fmt.Errorf("failed to read priv key file: %w", err) 315 } 316 return parsePriv(strings.TrimSpace(string(data))) 317 } 318 } 319 320 func parsePriv(data string) (*crypto.Secp256k1PrivateKey, error) { 321 if len(data) > 2 && data[:2] == "0x" { 322 data = data[2:] 323 } 324 b, err := hex.DecodeString(data) 325 if err != nil { 326 return nil, errors.New("p2p priv key is not formatted in hex chars") 327 } 328 p, err := crypto.UnmarshalSecp256k1PrivateKey(b) 329 if err != nil { 330 // avoid logging the priv key in the error, but hint at likely input length problem 331 return nil, fmt.Errorf("failed to parse priv key from %d bytes", len(b)) 332 } 333 return (p).(*crypto.Secp256k1PrivateKey), nil 334 } 335 336 func loadGossipOptions(conf *p2p.Config, ctx *cli.Context) error { 337 conf.MeshD = ctx.Int(flags.GossipMeshDName) 338 conf.MeshDLo = ctx.Int(flags.GossipMeshDloName) 339 conf.MeshDHi = ctx.Int(flags.GossipMeshDhiName) 340 conf.MeshDLazy = ctx.Int(flags.GossipMeshDlazyName) 341 conf.FloodPublish = ctx.Bool(flags.GossipFloodPublishName) 342 return nil 343 }