27
27
PATTERN_IPV4 = re .compile (r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$" )
28
28
PATTERN_IPV6 = re .compile (r"^\[([0-9a-z:]+)\]:(\d+)$" )
29
29
PATTERN_ONION = re .compile (r"^([a-z2-7]{56}\.onion):(\d+)$" )
30
+ PATTERN_I2P = re .compile (r"^([a-z2-7]{52}\.b32.i2p):(\d+)$" )
30
31
PATTERN_AGENT = re .compile (
31
32
r"^/Satoshi:("
32
33
r"0.14.(0|1|2|3|99)|"
@@ -66,7 +67,13 @@ def parseline(line: str) -> Union[dict, None]:
66
67
if m is None :
67
68
m = PATTERN_ONION .match (sline [0 ])
68
69
if m is None :
69
- return None
70
+ m = PATTERN_I2P .match (sline [0 ])
71
+ if m is None :
72
+ return None
73
+ else :
74
+ net = 'i2p'
75
+ ipstr = sortkey = m .group (1 )
76
+ port = int (m .group (2 ))
70
77
else :
71
78
net = 'onion'
72
79
ipstr = sortkey = m .group (1 )
@@ -141,6 +148,7 @@ def filterbyasn(asmap: ASMap, ips: list[dict], max_per_asn: dict, max_per_net: i
141
148
# Sift out ips by type
142
149
ips_ipv46 = [ip for ip in ips if ip ['net' ] in ['ipv4' , 'ipv6' ]]
143
150
ips_onion = [ip for ip in ips if ip ['net' ] == 'onion' ]
151
+ ips_i2p = [ip for ip in ips if ip ['net' ] == 'i2p' ]
144
152
145
153
# Filter IPv46 by ASN, and limit to max_per_net per network
146
154
result = []
@@ -164,6 +172,7 @@ def filterbyasn(asmap: ASMap, ips: list[dict], max_per_asn: dict, max_per_net: i
164
172
165
173
# Add back Onions (up to max_per_net)
166
174
result .extend (ips_onion [0 :max_per_net ])
175
+ result .extend (ips_i2p [0 :max_per_net ])
167
176
return result
168
177
169
178
def ip_stats (ips : list [dict ]) -> str :
@@ -173,7 +182,7 @@ def ip_stats(ips: list[dict]) -> str:
173
182
if ip is not None :
174
183
hist [ip ['net' ]] += 1
175
184
176
- return f"{ hist ['ipv4' ]:6d} { hist ['ipv6' ]:6d} { hist ['onion' ]:6d} "
185
+ return f"{ hist ['ipv4' ]:6d} { hist ['ipv6' ]:6d} { hist ['onion' ]:6d} { hist [ 'i2p' ]:6d } "
177
186
178
187
def parse_args ():
179
188
argparser = argparse .ArgumentParser (description = 'Generate a list of bitcoin node seed ip addresses.' )
@@ -195,7 +204,7 @@ def main():
195
204
ips = [parseline (line ) for line in lines ]
196
205
print ('Done.' , file = sys .stderr )
197
206
198
- print ('\x1b [7m IPv4 IPv6 Onion Pass \x1b [0m' , file = sys .stderr )
207
+ print ('\x1b [7m IPv4 IPv6 Onion I2P Pass \x1b [0m' , file = sys .stderr )
199
208
print (f'{ ip_stats (ips ):s} Initial' , file = sys .stderr )
200
209
# Skip entries with invalid address.
201
210
ips = [ip for ip in ips if ip is not None ]
@@ -209,11 +218,12 @@ def main():
209
218
# Require service bit 1.
210
219
ips = [ip for ip in ips if (ip ['service' ] & 1 ) == 1 ]
211
220
print (f'{ ip_stats (ips ):s} Require service bit 1' , file = sys .stderr )
212
- # Require at least 50% 30-day uptime for clearnet, 10% for onion.
221
+ # Require at least 50% 30-day uptime for clearnet, 10% for onion and i2p .
213
222
req_uptime = {
214
223
'ipv4' : 50 ,
215
224
'ipv6' : 50 ,
216
225
'onion' : 10 ,
226
+ 'i2p' : 10 ,
217
227
}
218
228
ips = [ip for ip in ips if ip ['uptime' ] > req_uptime [ip ['net' ]]]
219
229
print (f'{ ip_stats (ips ):s} Require minimum uptime' , file = sys .stderr )
0 commit comments