diff --git a/bootnode/config.go b/bootnode/config.go index f507abb..71904be 100644 --- a/bootnode/config.go +++ b/bootnode/config.go @@ -112,6 +112,12 @@ type Config struct { // EnableDiscv5 enables Discovery v5 protocol (default: true) EnableDiscv5 bool + // ServeAll disables EL/CL classification and fork-ID/digest filtering. Every + // discovered node is pooled (into every enabled table) and served to every + // requester, turning the bootnode into a plain discv5 rendezvous that relays + // all peers regardless of eth/eth2 fields. Default: false (classify + filter). + ServeAll bool + // SessionLifetime is the discv5 session lifetime (default: 12 hours) SessionLifetime time.Duration diff --git a/bootnode/service.go b/bootnode/service.go index b28017b..eeee4a3 100644 --- a/bootnode/service.go +++ b/bootnode/service.go @@ -254,8 +254,8 @@ func New(cfg *Config) (*Service, error) { Alpha: 3, LookupTimeout: 30 * time.Second, OnNodeFound: func(n *nodes.Node) bool { - // Filter by fork ID before adding to table - if n.Record() != nil && s.enrManager != nil { + // Filter by fork ID before adding to table (skipped in serve-all). + if !cfg.ServeAll && n.Record() != nil && s.enrManager != nil { if isEL, _ := s.enrManager.FilterELNode(n.Record()); !isEL { // Mark as bad node if err := cfg.Database.StoreBadNode(n.IDBytes(), db.LayerEL, "invalid_fork_id"); err != nil { @@ -337,8 +337,8 @@ func New(cfg *Config) (*Service, error) { Alpha: 3, LookupTimeout: 30 * time.Second, OnNodeFound: func(n *nodes.Node) bool { - // Filter by fork digest before adding to table - if n.Record() != nil && s.enrManager != nil { + // Filter by fork digest before adding to table (skipped in serve-all). + if !cfg.ServeAll && n.Record() != nil && s.enrManager != nil { if !s.enrManager.FilterCLNode(n.Record()) { // Mark as bad node if err := cfg.Database.StoreBadNode(n.IDBytes(), db.LayerCL, "invalid_fork_digest"); err != nil { @@ -1042,14 +1042,26 @@ func (s *Service) onNodeSeen(n *v5node.Node, timestamp time.Time) { if s.enrManager != nil { nodeID := n.ID() - if isEL, _ := s.enrManager.FilterELNode(n.Record()); isEL && s.elTable != nil && s.elNodeDB != nil { - // Look up the generic node from the table + isEL, _ := s.enrManager.FilterELNode(n.Record()) + isCL := s.enrManager.FilterCLNode(n.Record()) + + // Serve-all pools a node into every table, so refresh last-seen wherever it + // actually lives (checking both tables) rather than by classification. + if s.config.ServeAll { + isEL = s.elTable != nil + isCL = s.clTable != nil + } else if isEL { + // Classified mode is EL-xor-CL: keep the original precedence. + isCL = false + } + + if isEL && s.elTable != nil && s.elNodeDB != nil { if genericNode := s.elTable.Get(nodeID); genericNode != nil { genericNode.SetLastSeen(timestamp) // This marks it dirty s.elNodeDB.QueueUpdate(genericNode) } - } else if s.enrManager.FilterCLNode(n.Record()) && s.clTable != nil && s.clNodeDB != nil { - // Look up the generic node from the table + } + if isCL && s.clTable != nil && s.clNodeDB != nil { if genericNode := s.clTable.Get(nodeID); genericNode != nil { genericNode.SetLastSeen(timestamp) // This marks it dirty s.clNodeDB.QueueUpdate(genericNode) @@ -1069,7 +1081,8 @@ func (s *Service) onFindNodeV5(id *identity, msg *v5protocol.FindNode, sourceNod // A shared identity serves both layers under one ID, so classify a known // requester by its ENR and serve only its layer(s); an unclassifiable known // peer gets nothing, an unknown one (no ENR yet) falls through to both. - if id.servesEL && id.servesCL && sourceNode != nil && s.enrManager != nil { + // Serve-all skips this: every requester gets nodes from every served layer. + if !s.config.ServeAll && id.servesEL && id.servesCL && sourceNode != nil && s.enrManager != nil { sourceRecord := sourceNode.Record() serveEL, _ = s.enrManager.FilterELNode(sourceRecord) serveCL = s.enrManager.FilterCLNode(sourceRecord) @@ -1268,6 +1281,13 @@ func (s *Service) checkAndAddNode(n *v5node.Node) bool { isEL, _ := s.enrManager.FilterELNode(n.Record()) isCL := s.enrManager.FilterCLNode(n.Record()) + // Serve-all: skip classification and pool into every enabled table, so a node + // is admitted regardless of whether it advertises an eth/eth2 field. + if s.config.ServeAll { + isEL = s.elTable != nil + isCL = s.clTable != nil + } + // Add to appropriate table(s) added := false if isEL && s.elTable != nil { diff --git a/cmd/bootnodoor/main.go b/cmd/bootnodoor/main.go index 0c8042f..ad30df6 100644 --- a/cmd/bootnodoor/main.go +++ b/cmd/bootnodoor/main.go @@ -74,6 +74,9 @@ var ( enableEL bool enableCL bool + // Rendezvous mode + serveAll bool + // WebUI flags enableWebUI bool webUIHost string @@ -148,6 +151,9 @@ func init() { rootCmd.Flags().BoolVar(&enableEL, "enable-el", true, "Enable Execution Layer support (discv4 + discv5)") rootCmd.Flags().BoolVar(&enableCL, "enable-cl", true, "Enable Consensus Layer support (discv5)") + // Rendezvous mode + rootCmd.Flags().BoolVar(&serveAll, "serve-all", false, "Disable EL/CL classification and fork-ID filtering: pool and serve every discovered node to everyone (plain discv5 rendezvous)") + // WebUI rootCmd.Flags().BoolVar(&enableWebUI, "web-ui", false, "Enable web UI") rootCmd.Flags().StringVar(&webUIHost, "web-host", "0.0.0.0", "Web UI host") @@ -512,6 +518,7 @@ func runBootnode(cmd *cobra.Command, args []string) error { config.ENRPort = enrUDPPort config.EnableDiscv4 = enableDiscv4 config.EnableDiscv5 = enableDiscv5 + config.ServeAll = serveAll config.MaxActiveNodes = maxActiveNodes config.MaxNodesPerIP = maxNodesPerIP config.Logger = logger