mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-19 18:09:47 +01:00
420695c193
An IPv6 address from fc00::/8 could be either from the CJDNS network or from a private-unroutable-reserved segment of IPv6. A seed node with such an address must be from the CJDNS network, otherwise other peers will not be able to connect to it.
179 lines
5.5 KiB
Python
Executable File
179 lines
5.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2014-2021 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
'''
|
|
Script to generate list of seed nodes for chainparams.cpp.
|
|
|
|
This script expects two text files in the directory that is passed as an
|
|
argument:
|
|
|
|
nodes_main.txt
|
|
nodes_test.txt
|
|
|
|
These files must consist of lines in the format
|
|
|
|
<ip>:<port>
|
|
[<ipv6>]:<port>
|
|
<onion>.onion:<port>
|
|
<i2p>.b32.i2p:<port>
|
|
|
|
The output will be two data structures with the peers in binary format:
|
|
|
|
static const uint8_t chainparams_seed_{main,test}[]={
|
|
...
|
|
}
|
|
|
|
These should be pasted into `src/chainparamsseeds.h`.
|
|
'''
|
|
|
|
from base64 import b32decode
|
|
from enum import Enum
|
|
import struct
|
|
import sys
|
|
import os
|
|
import re
|
|
|
|
class BIP155Network(Enum):
|
|
IPV4 = 1
|
|
IPV6 = 2
|
|
TORV2 = 3 # no longer supported
|
|
TORV3 = 4
|
|
I2P = 5
|
|
CJDNS = 6
|
|
|
|
def name_to_bip155(addr):
|
|
'''Convert address string to BIP155 (networkID, addr) tuple.'''
|
|
if addr.endswith('.onion'):
|
|
vchAddr = b32decode(addr[0:-6], True)
|
|
if len(vchAddr) == 35:
|
|
assert vchAddr[34] == 3
|
|
return (BIP155Network.TORV3, vchAddr[:32])
|
|
elif len(vchAddr) == 10:
|
|
return (BIP155Network.TORV2, vchAddr)
|
|
else:
|
|
raise ValueError('Invalid onion %s' % vchAddr)
|
|
elif addr.endswith('.b32.i2p'):
|
|
vchAddr = b32decode(addr[0:-8] + '====', True)
|
|
if len(vchAddr) == 32:
|
|
return (BIP155Network.I2P, vchAddr)
|
|
else:
|
|
raise ValueError(f'Invalid I2P {vchAddr}')
|
|
elif '.' in addr: # IPv4
|
|
return (BIP155Network.IPV4, bytes((int(x) for x in addr.split('.'))))
|
|
elif ':' in addr: # IPv6 or CJDNS
|
|
sub = [[], []] # prefix, suffix
|
|
x = 0
|
|
addr = addr.split(':')
|
|
for i,comp in enumerate(addr):
|
|
if comp == '':
|
|
if i == 0 or i == (len(addr)-1): # skip empty component at beginning or end
|
|
continue
|
|
x += 1 # :: skips to suffix
|
|
assert(x < 2)
|
|
else: # two bytes per component
|
|
val = int(comp, 16)
|
|
sub[x].append(val >> 8)
|
|
sub[x].append(val & 0xff)
|
|
nullbytes = 16 - len(sub[0]) - len(sub[1])
|
|
assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0))
|
|
addr_bytes = bytes(sub[0] + ([0] * nullbytes) + sub[1])
|
|
if addr_bytes[0] == 0xfc:
|
|
# Assume that seeds with fc00::/8 addresses belong to CJDNS,
|
|
# not to the publicly unroutable "Unique Local Unicast" network, see
|
|
# RFC4193: https://datatracker.ietf.org/doc/html/rfc4193#section-8
|
|
return (BIP155Network.CJDNS, addr_bytes)
|
|
else:
|
|
return (BIP155Network.IPV6, addr_bytes)
|
|
else:
|
|
raise ValueError('Could not parse address %s' % addr)
|
|
|
|
def parse_spec(s):
|
|
'''Convert endpoint string to BIP155 (networkID, addr, port) tuple.'''
|
|
match = re.match(r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)
|
|
if match: # ipv6
|
|
host = match.group(1)
|
|
port = match.group(2)
|
|
elif s.count(':') > 1: # ipv6, no port
|
|
host = s
|
|
port = ''
|
|
else:
|
|
(host,_,port) = s.partition(':')
|
|
|
|
if not port:
|
|
port = 0
|
|
else:
|
|
port = int(port)
|
|
|
|
host = name_to_bip155(host)
|
|
|
|
if host[0] == BIP155Network.TORV2:
|
|
return None # TORV2 is no longer supported, so we ignore it
|
|
else:
|
|
return host + (port, )
|
|
|
|
def ser_compact_size(l):
|
|
r = b""
|
|
if l < 253:
|
|
r = struct.pack("B", l)
|
|
elif l < 0x10000:
|
|
r = struct.pack("<BH", 253, l)
|
|
elif l < 0x100000000:
|
|
r = struct.pack("<BI", 254, l)
|
|
else:
|
|
r = struct.pack("<BQ", 255, l)
|
|
return r
|
|
|
|
def bip155_serialize(spec):
|
|
'''
|
|
Serialize (networkID, addr, port) tuple to BIP155 binary format.
|
|
'''
|
|
r = b""
|
|
r += struct.pack('B', spec[0].value)
|
|
r += ser_compact_size(len(spec[1]))
|
|
r += spec[1]
|
|
r += struct.pack('>H', spec[2])
|
|
return r
|
|
|
|
def process_nodes(g, f, structname):
|
|
g.write('static const uint8_t %s[] = {\n' % structname)
|
|
for line in f:
|
|
comment = line.find('#')
|
|
if comment != -1:
|
|
line = line[0:comment]
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
|
|
spec = parse_spec(line)
|
|
if spec is None: # ignore this entry (e.g. no longer supported addresses like TORV2)
|
|
continue
|
|
blob = bip155_serialize(spec)
|
|
hoststr = ','.join(('0x%02x' % b) for b in blob)
|
|
g.write(f' {hoststr},\n')
|
|
g.write('};\n')
|
|
|
|
def main():
|
|
if len(sys.argv)<2:
|
|
print(('Usage: %s <path_to_nodes_txt>' % sys.argv[0]), file=sys.stderr)
|
|
sys.exit(1)
|
|
g = sys.stdout
|
|
indir = sys.argv[1]
|
|
g.write('#ifndef BITCOIN_CHAINPARAMSSEEDS_H\n')
|
|
g.write('#define BITCOIN_CHAINPARAMSSEEDS_H\n')
|
|
g.write('/**\n')
|
|
g.write(' * List of fixed seed nodes for the bitcoin network\n')
|
|
g.write(' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n')
|
|
g.write(' *\n')
|
|
g.write(' * Each line contains a BIP155 serialized (networkID, addr, port) tuple.\n')
|
|
g.write(' */\n')
|
|
with open(os.path.join(indir,'nodes_main.txt'), 'r', encoding="utf8") as f:
|
|
process_nodes(g, f, 'chainparams_seed_main')
|
|
g.write('\n')
|
|
with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f:
|
|
process_nodes(g, f, 'chainparams_seed_test')
|
|
g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n')
|
|
|
|
if __name__ == '__main__':
|
|
main()
|