Skip to content

Commit 37cdb5f

Browse files
committed
Merge bitcoin/bitcoin#30008: seeds: Pull additional nodes from my seeder and update fixed seeds
41ad84a seeds: Use fjahr's more up to date asmap (Ava Chow) d8fd1e0 seeds: Fixed seeds update (Ava Chow) f1f24d7 seeds: Add testnet4 fixed seeds file (Ava Chow) 8ace71c seeds: Remove manual onion and i2p seeds (Ava Chow) ed5b86c seeds: Add testnet instructions (Ava Chow) 0676515 seeds: Also pull from achow101 seeder (Ava Chow) 5bab317 makeseeds: Configurable minimum blocks for testnet4's smaller chain (Ava Chow) d2465df makeseeds: Shuffle ips after parsing (Ava Chow) af550b3 makeseeds: Support CJDNS (Ava Chow) d5a8c4c makeseeds: Update user agent regex (Ava Chow) Pull request description: The [DNS seeder](https://github.com/achow101/dnsseedrs) that I wrote collects statistics on node reliability in the same way that sipa's seeder does, and also outputs this information in the same file format. Thus it can also be used in our fixed seeds update scripts. My seeder additionally crawls onion v3, i2p, and cjdns, so will now be able to set those fixed seeds automatically rather than curating manual lists. In doing this update, I've found that `makeseeds.py` is missing newer versions from the regex as well as cjdns support; both of these have been updated. I also noticed that the testnet fixed seeds are all manually curated and sipa's seeder does not appear to publish any testnet data. Since I am also running the seeder for testnet, I've added the commands to generate testnet fixed seeds from my seeder's data too. Lastly, I've updated all of the fixed seeds. However, since my seeder has not found any cjdns nodes that met the reliability criteria (possibly due to connectivity issues present in those networks), I've left the previous manual seeds for that network. ACKs for top commit: fjahr: re-ACK 41ad84a virtu: ACK [41ad84a](bitcoin/bitcoin@41ad84a) Tree-SHA512: 6ba0141f053d9d6ae7d8c9574f061be38f3e65b28de1d6660c1885ab942623b5a0ec70754b4fcfc5d98fe970f5f179a940d5880b5061ed698f7932500e01d3ee
2 parents a05987d + 41ad84a commit 37cdb5f

File tree

8 files changed

+2072
-4177
lines changed

8 files changed

+2072
-4177
lines changed

contrib/seeds/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
seeds_main.txt
2+
seeds_test.txt
23
asmap-filled.dat

contrib/seeds/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@ and remove old versions as necessary (at a minimum when SeedsServiceFlags()
88
changes its default return value, as those are the services which seeds are added
99
to addrman with).
1010

11-
The seeds compiled into the release are created from sipa's DNS seed and AS map
11+
The seeds compiled into the release are created from sipa's and achow101's DNS seed and AS map
1212
data. Run the following commands from the `/contrib/seeds` directory:
1313

1414
```
1515
curl https://bitcoin.sipa.be/seeds.txt.gz | gzip -dc > seeds_main.txt
16-
curl https://bitcoin.sipa.be/asmap-filled.dat > asmap-filled.dat
16+
curl https://mainnet.achownodes.xyz/seeds.txt.gz | gzip -dc >> seeds_main.txt
17+
curl https://testnet.achownodes.xyz/seeds.txt.gz | gzip -dc > seeds_test.txt
18+
curl https://raw.githubusercontent.com/fjahr/asmap-data/main/latest_asmap.dat > asmap-filled.dat
1719
python3 makeseeds.py -a asmap-filled.dat -s seeds_main.txt > nodes_main.txt
1820
cat nodes_main_manual.txt >> nodes_main.txt
21+
python3 makeseeds.py -a asmap-filled.dat -s seeds_test.txt > nodes_test.txt
22+
# TODO: Uncomment when a seeder publishes seeds.txt.gz for testnet4
23+
# python3 makeseeds.py -a asmap-filled.dat -s seeds_testnet4.txt -m 30000 > nodes_testnet4.txt
1924
python3 generate-seeds.py . > ../../src/chainparamsseeds.h
2025
```

contrib/seeds/makeseeds.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import collections
1111
import ipaddress
1212
from pathlib import Path
13+
import random
1314
import re
1415
import sys
1516
from typing import Union
@@ -25,7 +26,7 @@
2526
'ipv6': 10,
2627
}
2728

28-
MIN_BLOCKS = 730000
29+
MIN_BLOCKS = 840000
2930

3031
PATTERN_IPV4 = re.compile(r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$")
3132
PATTERN_IPV6 = re.compile(r"^\[([0-9a-z:]+)\]:(\d+)$")
@@ -41,11 +42,13 @@
4142
r"0.19.(0|1|2|99)|"
4243
r"0.20.(0|1|2|99)|"
4344
r"0.21.(0|1|2|99)|"
44-
r"22.(0|1|99)|"
45-
r"23.(0|1|99)|"
46-
r"24.(0|1|99)|"
47-
r"25.(0|1|99)|"
48-
r"26.(0|99)|"
45+
r"22.(0|1|99).0|"
46+
r"23.(0|1|99).0|"
47+
r"24.(0|1|2|99).(0|1)|"
48+
r"25.(0|1|2|99).0|"
49+
r"26.(0|1|99).0|"
50+
r"27.(0|1|99).0|"
51+
r"28.(0|99).0|"
4952
r")")
5053

5154
def parseline(line: str) -> Union[dict, None]:
@@ -86,6 +89,8 @@ def parseline(line: str) -> Union[dict, None]:
8689
if m.group(1) in ['::']: # Not interested in localhost
8790
return None
8891
ipstr = m.group(1)
92+
if ipstr.startswith("fc"): # cjdns looks like ipv6 but always begins with fc
93+
net = "cjdns"
8994
sortkey = ipstr # XXX parse IPv6 into number, could use name_to_ipv6 from generate-seeds
9095
port = int(m.group(2))
9196
else:
@@ -152,6 +157,7 @@ def filterbyasn(asmap: ASMap, ips: list[dict], max_per_asn: dict, max_per_net: i
152157
ips_ipv46 = [ip for ip in ips if ip['net'] in ['ipv4', 'ipv6']]
153158
ips_onion = [ip for ip in ips if ip['net'] == 'onion']
154159
ips_i2p = [ip for ip in ips if ip['net'] == 'i2p']
160+
ips_cjdns = [ip for ip in ips if ip["net"] == "cjdns"]
155161

156162
# Filter IPv46 by ASN, and limit to max_per_net per network
157163
result = []
@@ -176,6 +182,7 @@ def filterbyasn(asmap: ASMap, ips: list[dict], max_per_asn: dict, max_per_net: i
176182
# Add back Onions (up to max_per_net)
177183
result.extend(ips_onion[0:max_per_net])
178184
result.extend(ips_i2p[0:max_per_net])
185+
result.extend(ips_cjdns[0:max_per_net])
179186
return result
180187

181188
def ip_stats(ips: list[dict]) -> str:
@@ -185,12 +192,13 @@ def ip_stats(ips: list[dict]) -> str:
185192
if ip is not None:
186193
hist[ip['net']] += 1
187194

188-
return f"{hist['ipv4']:6d} {hist['ipv6']:6d} {hist['onion']:6d} {hist['i2p']:6d}"
195+
return f"{hist['ipv4']:6d} {hist['ipv6']:6d} {hist['onion']:6d} {hist['i2p']:6d} {hist['cjdns']:6d}"
189196

190197
def parse_args():
191198
argparser = argparse.ArgumentParser(description='Generate a list of bitcoin node seed ip addresses.')
192199
argparser.add_argument("-a","--asmap", help='the location of the asmap asn database file (required)', required=True)
193200
argparser.add_argument("-s","--seeds", help='the location of the DNS seeds file (required)', required=True)
201+
argparser.add_argument("-m", "--minblocks", help="The minimum number of blocks each node must have", default=MIN_BLOCKS, type=int)
194202
return argparser.parse_args()
195203

196204
def main():
@@ -205,9 +213,10 @@ def main():
205213
with open(args.seeds, 'r', encoding='utf8') as f:
206214
lines = f.readlines()
207215
ips = [parseline(line) for line in lines]
216+
random.shuffle(ips)
208217
print('Done.', file=sys.stderr)
209218

210-
print('\x1b[7m IPv4 IPv6 Onion I2P Pass \x1b[0m', file=sys.stderr)
219+
print('\x1b[7m IPv4 IPv6 Onion I2P CJDNS Pass \x1b[0m', file=sys.stderr)
211220
print(f'{ip_stats(ips):s} Initial', file=sys.stderr)
212221
# Skip entries with invalid address.
213222
ips = [ip for ip in ips if ip is not None]
@@ -216,7 +225,7 @@ def main():
216225
ips = dedup(ips)
217226
print(f'{ip_stats(ips):s} After removing duplicates', file=sys.stderr)
218227
# Enforce minimal number of blocks.
219-
ips = [ip for ip in ips if ip['blocks'] >= MIN_BLOCKS]
228+
ips = [ip for ip in ips if ip['blocks'] >= args.minblocks]
220229
print(f'{ip_stats(ips):s} Enforce minimal number of blocks', file=sys.stderr)
221230
# Require service bit 1.
222231
ips = [ip for ip in ips if (ip['service'] & 1) == 1]
@@ -227,6 +236,7 @@ def main():
227236
'ipv6': 50,
228237
'onion': 10,
229238
'i2p' : 10,
239+
'cjdns': 10,
230240
}
231241
ips = [ip for ip in ips if ip['uptime'] > req_uptime[ip['net']]]
232242
print(f'{ip_stats(ips):s} Require minimum uptime', file=sys.stderr)
@@ -244,7 +254,7 @@ def main():
244254
# Sort the results by IP address (for deterministic output).
245255
ips.sort(key=lambda x: (x['net'], x['sortkey']))
246256
for ip in ips:
247-
if ip['net'] == 'ipv6':
257+
if ip['net'] == 'ipv6' or ip["net"] == "cjdns":
248258
print(f"[{ip['ip']}]:{ip['port']}", end="")
249259
else:
250260
print(f"{ip['ip']}:{ip['port']}", end="")

0 commit comments

Comments
 (0)