Commit | Line | Data |
---|---|---|
a60120e9 WL |
1 | #!/usr/bin/python |
2 | # Copyright (c) 2014 Wladmir J. van der Laan | |
02fe12dc | 3 | # Distributed under the MIT software license, see the accompanying |
a60120e9 WL |
4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ''' | |
6 | Script to generate list of seed nodes for chainparams.cpp. | |
7 | ||
8 | This script expects two text files in the directory that is passed as an | |
9 | argument: | |
10 | ||
11 | nodes_main.txt | |
12 | nodes_test.txt | |
13 | ||
14 | These files must consist of lines in the format | |
15 | ||
16 | <ip> | |
17 | <ip>:<port> | |
18 | [<ipv6>] | |
19 | [<ipv6>]:<port> | |
20 | <onion>.onion | |
21 | 0xDDBBCCAA (IPv4 little-endian old pnSeeds format) | |
22 | ||
23 | The output will be two data structures with the peers in binary format: | |
24 | ||
25 | static SeedSpec6 pnSeed6_main[]={ | |
26 | ... | |
27 | } | |
28 | static SeedSpec6 pnSeed6_test[]={ | |
29 | ... | |
30 | } | |
31 | ||
32 | These should be pasted into `src/chainparamsseeds.h`. | |
33 | ''' | |
34 | from __future__ import print_function, division | |
35 | from base64 import b32decode | |
36 | from binascii import a2b_hex | |
37 | import sys, os | |
38 | import re | |
39 | ||
40 | # ipv4 in ipv6 prefix | |
41 | pchIPv4 = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff]) | |
42 | # tor-specific ipv6 prefix | |
43 | pchOnionCat = bytearray([0xFD,0x87,0xD8,0x7E,0xEB,0x43]) | |
44 | ||
45 | def name_to_ipv6(addr): | |
46 | if len(addr)>6 and addr.endswith('.onion'): | |
47 | vchAddr = b32decode(addr[0:-6], True) | |
48 | if len(vchAddr) != 16-len(pchOnionCat): | |
49 | raise ValueError('Invalid onion %s' % s) | |
50 | return pchOnionCat + vchAddr | |
51 | elif '.' in addr: # IPv4 | |
52 | return pchIPv4 + bytearray((int(x) for x in addr.split('.'))) | |
53 | elif ':' in addr: # IPv6 | |
54 | sub = [[], []] # prefix, suffix | |
55 | x = 0 | |
56 | addr = addr.split(':') | |
57 | for i,comp in enumerate(addr): | |
58 | if comp == '': | |
59 | if i == 0 or i == (len(addr)-1): # skip empty component at beginning or end | |
60 | continue | |
61 | x += 1 # :: skips to suffix | |
62 | assert(x < 2) | |
63 | else: # two bytes per component | |
64 | val = int(comp, 16) | |
65 | sub[x].append(val >> 8) | |
66 | sub[x].append(val & 0xff) | |
67 | nullbytes = 16 - len(sub[0]) - len(sub[1]) | |
68 | assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0)) | |
69 | return bytearray(sub[0] + ([0] * nullbytes) + sub[1]) | |
70 | elif addr.startswith('0x'): # IPv4-in-little-endian | |
71 | return pchIPv4 + bytearray(reversed(a2b_hex(addr[2:]))) | |
72 | else: | |
73 | raise ValueError('Could not parse address %s' % addr) | |
74 | ||
75 | def parse_spec(s, defaultport): | |
76 | match = re.match('\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s) | |
77 | if match: # ipv6 | |
78 | host = match.group(1) | |
79 | port = match.group(2) | |
41bbc85e WL |
80 | elif s.count(':') > 1: # ipv6, no port |
81 | host = s | |
82 | port = '' | |
a60120e9 WL |
83 | else: |
84 | (host,_,port) = s.partition(':') | |
85 | ||
86 | if not port: | |
87 | port = defaultport | |
88 | else: | |
89 | port = int(port) | |
90 | ||
91 | host = name_to_ipv6(host) | |
92 | ||
93 | return (host,port) | |
94 | ||
95 | def process_nodes(g, f, structname, defaultport): | |
96 | g.write('static SeedSpec6 %s[] = {\n' % structname) | |
97 | first = True | |
98 | for line in f: | |
99 | comment = line.find('#') | |
100 | if comment != -1: | |
101 | line = line[0:comment] | |
102 | line = line.strip() | |
103 | if not line: | |
104 | continue | |
105 | if not first: | |
106 | g.write(',\n') | |
107 | first = False | |
108 | ||
109 | (host,port) = parse_spec(line, defaultport) | |
110 | hoststr = ','.join(('0x%02x' % b) for b in host) | |
111 | g.write(' {{%s}, %i}' % (hoststr, port)) | |
112 | g.write('\n};\n') | |
113 | ||
114 | def main(): | |
115 | if len(sys.argv)<2: | |
116 | print(('Usage: %s <path_to_nodes_txt>' % sys.argv[0]), file=sys.stderr) | |
117 | exit(1) | |
118 | g = sys.stdout | |
119 | indir = sys.argv[1] | |
84738627 PJ |
120 | g.write('#ifndef BITCOIN_CHAINPARAMSSEEDS_H\n') |
121 | g.write('#define BITCOIN_CHAINPARAMSSEEDS_H\n') | |
02fe12dc MF |
122 | g.write('/**\n') |
123 | g.write(' * List of fixed seed nodes for the bitcoin network\n') | |
41bbc85e | 124 | g.write(' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n') |
02fe12dc MF |
125 | g.write(' *\n') |
126 | g.write(' * Each line contains a 16-byte IPv6 address and a port.\n') | |
127 | g.write(' * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.\n') | |
128 | g.write(' */\n') | |
a60120e9 WL |
129 | with open(os.path.join(indir,'nodes_main.txt'),'r') as f: |
130 | process_nodes(g, f, 'pnSeed6_main', 8333) | |
131 | g.write('\n') | |
132 | with open(os.path.join(indir,'nodes_test.txt'),'r') as f: | |
133 | process_nodes(g, f, 'pnSeed6_test', 18333) | |
84738627 | 134 | g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') |
a60120e9 WL |
135 | |
136 | if __name__ == '__main__': | |
137 | main() | |
138 |