1
1
#!/usr/bin/env python3
2
- # Copyright (c) 2014-2017 Wladimir J. van der Laan
2
+ # Copyright (c) 2014-2021 The Bitcoin Core developers
3
3
# Distributed under the MIT software license, see the accompanying
4
4
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
'''
13
13
14
14
These files must consist of lines in the format
15
15
16
- <ip>
17
16
<ip>:<port>
18
- [<ipv6>]
19
17
[<ipv6>]:<port>
20
- <onion>.onion
21
- 0xDDBBCCAA (IPv4 little-endian old pnSeeds format)
18
+ <onion>.onion:<port>
22
19
23
20
The output will be two data structures with the peers in binary format:
24
21
25
- static SeedSpec6 pnSeed6_main[]={
26
- ...
27
- }
28
- static SeedSpec6 pnSeed6_test[]={
22
+ static const uint8_t chainparams_seed_{main,test}[]={
29
23
...
30
24
}
31
25
32
26
These should be pasted into `src/chainparamsseeds.h`.
33
27
'''
34
28
35
29
from base64 import b32decode
36
- from binascii import a2b_hex
30
+ from enum import Enum
31
+ import struct
37
32
import sys
38
33
import os
39
34
import re
40
35
41
- # ipv4 in ipv6 prefix
42
- pchIPv4 = bytearray ([0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0xff , 0xff ])
43
- # tor-specific ipv6 prefix
44
- pchOnionCat = bytearray ([0xFD ,0x87 ,0xD8 ,0x7E ,0xEB ,0x43 ])
45
-
46
- def name_to_ipv6 (addr ):
47
- if len (addr )> 6 and addr .endswith ('.onion' ):
36
+ class BIP155Network (Enum ):
37
+ IPV4 = 1
38
+ IPV6 = 2
39
+ TORV2 = 3
40
+ TORV3 = 4
41
+ I2P = 5
42
+ CJDNS = 6
43
+
44
+ def name_to_bip155 (addr ):
45
+ '''Convert address string to BIP155 (networkID, addr) tuple.'''
46
+ if addr .endswith ('.onion' ):
48
47
vchAddr = b32decode (addr [0 :- 6 ], True )
49
- if len (vchAddr ) != 16 - len (pchOnionCat ):
48
+ if len (vchAddr ) == 10 :
49
+ return (BIP155Network .TORV2 , vchAddr )
50
+ elif len (vchAddr ) == 35 :
51
+ assert (vchAddr [34 ] == 3 )
52
+ return (BIP155Network .TORV3 , vchAddr [:32 ])
53
+ else :
50
54
raise ValueError ('Invalid onion %s' % vchAddr )
51
- return pchOnionCat + vchAddr
52
55
elif '.' in addr : # IPv4
53
- return pchIPv4 + bytearray ((int (x ) for x in addr .split ('.' )))
56
+ return ( BIP155Network . IPV4 , bytes ((int (x ) for x in addr .split ('.' ) )))
54
57
elif ':' in addr : # IPv6
55
58
sub = [[], []] # prefix, suffix
56
59
x = 0
@@ -67,13 +70,12 @@ def name_to_ipv6(addr):
67
70
sub [x ].append (val & 0xff )
68
71
nullbytes = 16 - len (sub [0 ]) - len (sub [1 ])
69
72
assert ((x == 0 and nullbytes == 0 ) or (x == 1 and nullbytes > 0 ))
70
- return bytearray (sub [0 ] + ([0 ] * nullbytes ) + sub [1 ])
71
- elif addr .startswith ('0x' ): # IPv4-in-little-endian
72
- return pchIPv4 + bytearray (reversed (a2b_hex (addr [2 :])))
73
+ return (BIP155Network .IPV6 , bytes (sub [0 ] + ([0 ] * nullbytes ) + sub [1 ]))
73
74
else :
74
75
raise ValueError ('Could not parse address %s' % addr )
75
76
76
- def parse_spec (s , defaultport ):
77
+ def parse_spec (s ):
78
+ '''Convert endpoint string to BIP155 (networkID, addr, port) tuple.'''
77
79
match = re .match (r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$' , s )
78
80
if match : # ipv6
79
81
host = match .group (1 )
@@ -85,32 +87,52 @@ def parse_spec(s, defaultport):
85
87
(host ,_ ,port ) = s .partition (':' )
86
88
87
89
if not port :
88
- port = defaultport
90
+ port = 0
89
91
else :
90
92
port = int (port )
91
93
92
- host = name_to_ipv6 (host )
94
+ host = name_to_bip155 (host )
93
95
94
- return ( host , port )
96
+ return host + ( port , )
95
97
96
- def process_nodes (g , f , structname , defaultport ):
97
- g .write ('static SeedSpec6 %s[] = {\n ' % structname )
98
- first = True
98
+ def ser_compact_size (l ):
99
+ r = b""
100
+ if l < 253 :
101
+ r = struct .pack ("B" , l )
102
+ elif l < 0x10000 :
103
+ r = struct .pack ("<BH" , 253 , l )
104
+ elif l < 0x100000000 :
105
+ r = struct .pack ("<BI" , 254 , l )
106
+ else :
107
+ r = struct .pack ("<BQ" , 255 , l )
108
+ return r
109
+
110
+ def bip155_serialize (spec ):
111
+ '''
112
+ Serialize (networkID, addr, port) tuple to BIP155 binary format.
113
+ '''
114
+ r = b""
115
+ r += struct .pack ('B' , spec [0 ].value )
116
+ r += ser_compact_size (len (spec [1 ]))
117
+ r += spec [1 ]
118
+ r += struct .pack ('>H' , spec [2 ])
119
+ return r
120
+
121
+ def process_nodes (g , f , structname ):
122
+ g .write ('static const uint8_t %s[] = {\n ' % structname )
99
123
for line in f :
100
124
comment = line .find ('#' )
101
125
if comment != - 1 :
102
126
line = line [0 :comment ]
103
127
line = line .strip ()
104
128
if not line :
105
129
continue
106
- if not first :
107
- g .write (',\n ' )
108
- first = False
109
130
110
- (host ,port ) = parse_spec (line , defaultport )
111
- hoststr = ',' .join (('0x%02x' % b ) for b in host )
112
- g .write (' {{%s}, %i}' % (hoststr , port ))
113
- g .write ('\n };\n ' )
131
+ spec = parse_spec (line )
132
+ blob = bip155_serialize (spec )
133
+ hoststr = ',' .join (('0x%02x' % b ) for b in blob )
134
+ g .write (f' { hoststr } ,\n ' )
135
+ g .write ('};\n ' )
114
136
115
137
def main ():
116
138
if len (sys .argv )< 2 :
@@ -124,14 +146,13 @@ def main():
124
146
g .write (' * List of fixed seed nodes for the bitcoin network\n ' )
125
147
g .write (' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n ' )
126
148
g .write (' *\n ' )
127
- g .write (' * Each line contains a 16-byte IPv6 address and a port.\n ' )
128
- g .write (' * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.\n ' )
149
+ g .write (' * Each line contains a BIP155 serialized (networkID, addr, port) tuple.\n ' )
129
150
g .write (' */\n ' )
130
151
with open (os .path .join (indir ,'nodes_main.txt' ), 'r' , encoding = "utf8" ) as f :
131
- process_nodes (g , f , 'pnSeed6_main' , 8333 )
152
+ process_nodes (g , f , 'chainparams_seed_main' )
132
153
g .write ('\n ' )
133
154
with open (os .path .join (indir ,'nodes_test.txt' ), 'r' , encoding = "utf8" ) as f :
134
- process_nodes (g , f , 'pnSeed6_test' , 18333 )
155
+ process_nodes (g , f , 'chainparams_seed_test' )
135
156
g .write ('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n ' )
136
157
137
158
if __name__ == '__main__' :
0 commit comments