2727PATTERN_IPV4 = re .compile (r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$" )
2828PATTERN_IPV6 = re .compile (r"^\[([0-9a-z:]+)\]:(\d+)$" )
2929PATTERN_ONION = re .compile (r"^([a-z2-7]{56}\.onion):(\d+)$" )
30+ PATTERN_I2P = re .compile (r"^([a-z2-7]{52}\.b32.i2p):(\d+)$" )
3031PATTERN_AGENT = re .compile (
3132 r"^/Satoshi:("
3233 r"0.14.(0|1|2|3|99)|"
@@ -66,7 +67,13 @@ def parseline(line: str) -> Union[dict, None]:
6667 if m is None :
6768 m = PATTERN_ONION .match (sline [0 ])
6869 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 ))
7077 else :
7178 net = 'onion'
7279 ipstr = sortkey = m .group (1 )
@@ -141,6 +148,7 @@ def filterbyasn(asmap: ASMap, ips: list[dict], max_per_asn: dict, max_per_net: i
141148 # Sift out ips by type
142149 ips_ipv46 = [ip for ip in ips if ip ['net' ] in ['ipv4' , 'ipv6' ]]
143150 ips_onion = [ip for ip in ips if ip ['net' ] == 'onion' ]
151+ ips_i2p = [ip for ip in ips if ip ['net' ] == 'i2p' ]
144152
145153 # Filter IPv46 by ASN, and limit to max_per_net per network
146154 result = []
@@ -164,6 +172,7 @@ def filterbyasn(asmap: ASMap, ips: list[dict], max_per_asn: dict, max_per_net: i
164172
165173 # Add back Onions (up to max_per_net)
166174 result .extend (ips_onion [0 :max_per_net ])
175+ result .extend (ips_i2p [0 :max_per_net ])
167176 return result
168177
169178def ip_stats (ips : list [dict ]) -> str :
@@ -173,7 +182,7 @@ def ip_stats(ips: list[dict]) -> str:
173182 if ip is not None :
174183 hist [ip ['net' ]] += 1
175184
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 } "
177186
178187def parse_args ():
179188 argparser = argparse .ArgumentParser (description = 'Generate a list of bitcoin node seed ip addresses.' )
@@ -195,7 +204,7 @@ def main():
195204 ips = [parseline (line ) for line in lines ]
196205 print ('Done.' , file = sys .stderr )
197206
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 )
199208 print (f'{ ip_stats (ips ):s} Initial' , file = sys .stderr )
200209 # Skip entries with invalid address.
201210 ips = [ip for ip in ips if ip is not None ]
@@ -209,11 +218,12 @@ def main():
209218 # Require service bit 1.
210219 ips = [ip for ip in ips if (ip ['service' ] & 1 ) == 1 ]
211220 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 .
213222 req_uptime = {
214223 'ipv4' : 50 ,
215224 'ipv6' : 50 ,
216225 'onion' : 10 ,
226+ 'i2p' : 10 ,
217227 }
218228 ips = [ip for ip in ips if ip ['uptime' ] > req_uptime [ip ['net' ]]]
219229 print (f'{ ip_stats (ips ):s} Require minimum uptime' , file = sys .stderr )
0 commit comments