Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
75902ec
p2p/addrman: port Bitcoin Core bucketing primitives
andrepatta Apr 22, 2026
129ac7b
p2p/addrman: implement Add/Good/Attempt/Select and persistence
andrepatta Apr 22, 2026
32870e0
p2p/addrman: unit tests and Select benchmark
andrepatta Apr 22, 2026
51cf199
p2p/protocols/disc: parallax-disc/1 subprotocol wire format and handl…
andrepatta Apr 22, 2026
fb602cf
p2p: wire addrman into Server behind --experimental-addrman
andrepatta Apr 23, 2026
3fcd00f
p2p/protocols/disc: activate gossip — addrman-backed Backend, quorum,…
andrepatta Apr 23, 2026
9154392
p2p, node: wire parallax-disc/1 protocol and Good/Attempt lifecycle
andrepatta Apr 23, 2026
cfe225d
p2p/addrman, p2p: dual-stack bridging and source-aware prioritization
andrepatta Apr 23, 2026
b64d96f
p2p/addrman, node: admin RPC methods — addnode/removenode/status/rese…
andrepatta Apr 23, 2026
6d593ff
parallax-cli, cmd/devp2p: addnode/addrbook subcommands and parallax-d…
andrepatta Apr 23, 2026
9038ee8
p2p/rlpx/bip324handshake: v2 RLPx handshake primitive behind --experi…
andrepatta Apr 23, 2026
1ad7fa8
p2p, node, cmd: fully wire BIP324-style v2 handshake into Server
andrepatta Apr 23, 2026
a9e16d5
p2p, cmd, docs: collapse PIP-0006 flags into --legacy-discovery
andrepatta Apr 23, 2026
1f994ae
p2p, node: v2-only posture exposes clean nodeInfo + startup log
andrepatta Apr 23, 2026
7d0e8f8
p2p: trim external-address-updated log to just ip + port
andrepatta Apr 23, 2026
e2390d8
p2p: log P2P address as address=ip:port (one field, not two)
andrepatta Apr 23, 2026
60e164a
p2p/protocols/disc: add PeerInfo callback so admin.peers stops showin…
andrepatta Apr 23, 2026
e5448b0
p2p/protocols/disc: PeerInfo reports handshake variant (v2 vs legacy+v2)
andrepatta Apr 23, 2026
636d384
p2p: ip:port bootnodes + v2 peer dedup on (IP, port)
andrepatta Apr 23, 2026
e393dfe
p2p: peer Enode/ENR/ID marshal as null for v2 sessions
andrepatta Apr 23, 2026
c00d99d
p2p: keep session-scoped ID visible for v2 peers (only Enode/ENR null)
andrepatta Apr 23, 2026
ce2a8d9
p2p: null out PeerInfo.ID for v2 peers
andrepatta Apr 23, 2026
3828fbd
fix lint: goimports, unconvert, unused, vet
andrepatta Apr 23, 2026
bedffb5
p2p/simulations: skip parallax-disc/1 on sim nodes
andrepatta Apr 23, 2026
cd8d11c
cmd/parallax-cli: loosen addpeer error-hint assertion
andrepatta Apr 23, 2026
822cb1d
cmd/devp2p: parallax-disc crawl supports v2 handshake and ip:port input
andrepatta Apr 23, 2026
c28f0b4
cmd/devp2p: parallax-disc crawl is now a multi-hop stateful walker
andrepatta Apr 23, 2026
9417b7c
cmd/devp2p: tests for parallax-disc seed parser, walker dedup, state I/O
andrepatta Apr 23, 2026
8bb6b99
cmd/devp2p: dns-seed publisher (compile, to-zonefile, to-cloudflare, …
andrepatta Apr 23, 2026
ee07568
cmd/devp2p: tests for dns-seed compile and zonefile rendering
andrepatta Apr 23, 2026
0682c92
p2p, cmd: plain-DNS seed consumer with --dnsseed flag
andrepatta Apr 23, 2026
8e2ccce
p2p: allow up to 4 concurrent inbound attempts per IP
andrepatta Apr 23, 2026
c770636
fix lint: goimports, unconvert, unused
andrepatta Apr 23, 2026
7b8ae3b
cmd/devp2p: parallax-disc crawl honors --timeout via re-probe loop
andrepatta Apr 23, 2026
6a8a53d
p2p: split v1/v2 bootnodes, ENR-driven v2 dial, addrman upgrades
andrepatta Apr 23, 2026
8a5e444
cmd/devp2p: report pipv2 adoption in crawl summary
andrepatta Apr 23, 2026
d7ba2e4
cmd/devp2p: log each crawl entry as it's processed
andrepatta Apr 23, 2026
d3d3be7
cmd/devp2p: log each parallax-disc probe as it runs
andrepatta Apr 23, 2026
acde4c6
cmd/devp2p: clearer error when dns-seed deploy is fed a zonefile
andrepatta Apr 23, 2026
51e2977
p2p/netparams: empty v2 disc bootnodes
andrepatta Apr 23, 2026
69efcc2
internal/api: add net_peers api
andrepatta Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion cmd/devp2p/crawl.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,54 @@ type parallaxENREntry struct {

func (e parallaxENREntry) ENRKey() string { return "parallax" }

// pipv2ENREntry tests for the "pipv2" key — the v2-transport
// capability flag set by nodes that accept BIP324-style handshakes.
// Used by the crawler to report v2 adoption across the discovered
// network.
type pipv2ENREntry struct {
Rest []rlp.RawValue `rlp:"tail"`
}

func (e pipv2ENREntry) ENRKey() string { return "pipv2" }

// hasPipV2 reports whether n's ENR advertises v2-transport support.
func hasPipV2(n *enode.Node) bool {
if n == nil {
return false
}
var e pipv2ENREntry
return n.Load(&e) == nil
}

// logCrawlEntry emits a per-node info line describing the crawler's
// decision for n. One line per processed entry so operators can see
// the crawl unfold live rather than wait for the periodic status
// ticker.
func logCrawlEntry(status int, n *enode.Node) {
if n == nil {
return
}
action := "updated"
switch status {
case nodeAdded:
action = "added"
case nodeRemoved:
action = "removed"
case nodeSkipRecent:
action = "skip_recent"
case nodeSkipIncompat:
action = "skip_incompat"
}
logging.Info("Crawl entry",
"action", action,
"id", n.ID(),
"ip", n.IP(),
"tcp", n.TCP(),
"udp", n.UDP(),
"seq", n.Seq(),
"pipv2", hasPipV2(n))
}

type crawler struct {
input nodeSet
output nodeSet
Expand Down Expand Up @@ -112,7 +160,8 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
for {
select {
case n := <-c.ch:
switch c.updateNode(n) {
status := c.updateNode(n)
switch status {
case nodeSkipIncompat:
atomic.AddUint64(&skipped, 1)
case nodeSkipRecent:
Expand All @@ -124,6 +173,7 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
default:
atomic.AddUint64(&updated, 1)
}
logCrawlEntry(status, n)
case <-c.closed:
return
}
Expand Down Expand Up @@ -166,6 +216,18 @@ loop:
<-doneCh
}
wg.Wait()

var v2Count int
for _, n := range c.output {
if hasPipV2(n.N) {
v2Count++
}
}
logging.Info("Crawl complete",
"total", len(c.output),
"pipv2", v2Count,
"v1_only", len(c.output)-v2Count)

return c.output
}

Expand Down
69 changes: 69 additions & 0 deletions cmd/devp2p/dns_cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,75 @@ func (c *cloudflareClient) checkZone(name string) error {
return nil
}

// deploySeedZone reconciles the SeedZone's A/AAAA records at the
// zone's apex. One Cloudflare DNS record per (family, IP) — Cloudflare
// returns multiple A/AAAA records as a round-robin set, which is what
// clients of `seed.prlxdisc.org` will resolve. Stale apex A/AAAA
// records (matching family, scoped to the zone apex) that are not in
// the new SeedZone are deleted; this is the same idempotent reconcile
// pattern as uploadRecords above, just specialized for A/AAAA at the
// zone apex.
func (c *cloudflareClient) deploySeedZone(z *SeedZone, ttl int) error {
if err := c.checkZone(z.Name); err != nil {
return err
}

// Build the desired set: name → list of (family, IP) entries.
type want struct {
family string
ip string
}
desired := make(map[string]struct{}, len(z.Records))
wants := make([]want, 0, len(z.Records))
for _, r := range z.Records {
desired[r.Family+"|"+r.IP] = struct{}{}
wants = append(wants, want{family: r.Family, ip: r.IP})
}

// Pull existing A and AAAA records at the apex (Name == z.Name).
existing := map[string]cloudflare.DNSRecord{} // key = family+"|"+ip
for _, kind := range []string{"A", "AAAA"} {
entries, err := c.DNSRecords(context.Background(), c.zoneID, cloudflare.DNSRecord{Type: kind, Name: z.Name})
if err != nil {
return fmt.Errorf("list %s records: %w", kind, err)
}
for _, e := range entries {
if !strings.EqualFold(e.Name, z.Name) {
continue
}
existing[e.Type+"|"+e.Content] = e
}
}

// Create anything missing.
created, skipped := 0, 0
for _, w := range wants {
key := w.family + "|" + w.ip
if _, ok := existing[key]; ok {
skipped++
continue
}
rec := cloudflare.DNSRecord{Type: w.family, Name: z.Name, Content: w.ip, TTL: ttl}
if _, err := c.CreateDNSRecord(context.Background(), c.zoneID, rec); err != nil {
return fmt.Errorf("create %s %s: %w", w.family, w.ip, err)
}
created++
}
// Delete anything stale.
deleted := 0
for key, e := range existing {
if _, ok := desired[key]; ok {
continue
}
if err := c.DeleteDNSRecord(context.Background(), c.zoneID, e.ID); err != nil {
return fmt.Errorf("delete %s %s: %w", e.Type, e.Content, err)
}
deleted++
}
logging.Info("Cloudflare seed zone deployed", "name", z.Name, "created", created, "skipped", skipped, "deleted", deleted)
return nil
}

// uploadRecords updates the TXT records at a particular subdomain. All non-root records
// will have a TTL of "infinity" and all existing records not in the new map will be
// nuked!
Expand Down
52 changes: 52 additions & 0 deletions cmd/devp2p/dns_route53.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,58 @@ func (c *route53Client) deleteDomain(name string) error {
return c.submitChanges(changes, comment)
}

// deploySeedZone reconciles the SeedZone's A and AAAA records at the
// zone's apex. One RRSet per family (all IPv4s in one A RRSet, all
// IPv6s in one AAAA RRSet) — matches Route53's billing model and what
// plan.md specifies. Records are submitted via UPSERT (idempotent) so
// re-running with the same SeedZone is a no-op at the API level.
func (c *route53Client) deploySeedZone(z *SeedZone, ttl int64) error {
if err := c.checkZone(z.Name); err != nil {
return err
}
var ipv4s, ipv6s []string
for _, r := range z.Records {
switch r.Family {
case "A":
ipv4s = append(ipv4s, r.IP)
case "AAAA":
ipv6s = append(ipv6s, r.IP)
}
}
sort.Strings(ipv4s)
sort.Strings(ipv6s)

var changes []types.Change
if len(ipv4s) > 0 {
changes = append(changes, newAddrChange(types.RRTypeA, z.Name, ttl, ipv4s))
}
if len(ipv6s) > 0 {
changes = append(changes, newAddrChange(types.RRTypeAaaa, z.Name, ttl, ipv6s))
}
if len(changes) == 0 {
return errors.New("seed zone has no IPv4 or IPv6 records — refusing to deploy an empty zone")
}
comment := fmt.Sprintf("dns-seed update of %s at seq %d", z.Name, z.Seq)
return c.submitChanges(changes, comment)
}

// newAddrChange builds an UPSERT change for one A or AAAA RRSet.
func newAddrChange(rrtype types.RRType, name string, ttl int64, values []string) types.Change {
rrs := make([]types.ResourceRecord, 0, len(values))
for _, v := range values {
rrs = append(rrs, types.ResourceRecord{Value: aws.String(v)})
}
return types.Change{
Action: types.ChangeActionUpsert,
ResourceRecordSet: &types.ResourceRecordSet{
Type: rrtype,
Name: aws.String(name),
TTL: aws.Int64(ttl),
ResourceRecords: rrs,
},
}
}

// submitChanges submits the given DNS changes to Route53.
func (c *route53Client) submitChanges(changes []types.Change, comment string) error {
if len(changes) == 0 {
Expand Down
Loading
Loading