aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorW. J. van der Laan <laanwj@protonmail.com>2021-03-31 13:29:21 +0200
committerW. J. van der Laan <laanwj@protonmail.com>2021-04-05 13:59:40 +0200
commit06030f7a42dea33c5120504dcd99d1714883f271 (patch)
tree06b969f0698f538ae994bd4e6376d0b15bc644c4 /contrib
parentf9e86d89660002d3dd17211d8b030a47b95d2fa2 (diff)
contrib: generate-seeds.py generates output in BIP155 format
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/seeds/generate-seeds.py103
1 files changed, 62 insertions, 41 deletions
diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py
index 7630a7a4fa..d95069277d 100755
--- a/contrib/seeds/generate-seeds.py
+++ b/contrib/seeds/generate-seeds.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2017 Wladimir J. van der Laan
+# 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.
'''
@@ -13,19 +13,13 @@ argument:
These files must consist of lines in the format
- <ip>
<ip>:<port>
- [<ipv6>]
[<ipv6>]:<port>
- <onion>.onion
- 0xDDBBCCAA (IPv4 little-endian old pnSeeds format)
+ <onion>.onion:<port>
The output will be two data structures with the peers in binary format:
- static SeedSpec6 pnSeed6_main[]={
- ...
- }
- static SeedSpec6 pnSeed6_test[]={
+ static const uint8_t chainparams_seed_{main,test}[]={
...
}
@@ -33,24 +27,33 @@ These should be pasted into `src/chainparamsseeds.h`.
'''
from base64 import b32decode
-from binascii import a2b_hex
+from enum import Enum
+import struct
import sys
import os
import re
-# ipv4 in ipv6 prefix
-pchIPv4 = bytearray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff])
-# tor-specific ipv6 prefix
-pchOnionCat = bytearray([0xFD,0x87,0xD8,0x7E,0xEB,0x43])
-
-def name_to_ipv6(addr):
- if len(addr)>6 and addr.endswith('.onion'):
+class BIP155Network(Enum):
+ IPV4 = 1
+ IPV6 = 2
+ TORV2 = 3
+ 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) != 16-len(pchOnionCat):
+ if len(vchAddr) == 10:
+ return (BIP155Network.TORV2, vchAddr)
+ elif len(vchAddr) == 35:
+ assert(vchAddr[34] == 3)
+ return (BIP155Network.TORV3, vchAddr[:32])
+ else:
raise ValueError('Invalid onion %s' % vchAddr)
- return pchOnionCat + vchAddr
elif '.' in addr: # IPv4
- return pchIPv4 + bytearray((int(x) for x in addr.split('.')))
+ return (BIP155Network.IPV4, bytes((int(x) for x in addr.split('.'))))
elif ':' in addr: # IPv6
sub = [[], []] # prefix, suffix
x = 0
@@ -67,13 +70,12 @@ def name_to_ipv6(addr):
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))
- return bytearray(sub[0] + ([0] * nullbytes) + sub[1])
- elif addr.startswith('0x'): # IPv4-in-little-endian
- return pchIPv4 + bytearray(reversed(a2b_hex(addr[2:])))
+ return (BIP155Network.IPV6, bytes(sub[0] + ([0] * nullbytes) + sub[1]))
else:
raise ValueError('Could not parse address %s' % addr)
-def parse_spec(s, defaultport):
+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)
@@ -85,17 +87,39 @@ def parse_spec(s, defaultport):
(host,_,port) = s.partition(':')
if not port:
- port = defaultport
+ port = 0
else:
port = int(port)
- host = name_to_ipv6(host)
+ host = name_to_bip155(host)
- return (host,port)
+ return host + (port, )
-def process_nodes(g, f, structname, defaultport):
- g.write('static SeedSpec6 %s[] = {\n' % structname)
- first = True
+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:
@@ -103,14 +127,12 @@ def process_nodes(g, f, structname, defaultport):
line = line.strip()
if not line:
continue
- if not first:
- g.write(',\n')
- first = False
- (host,port) = parse_spec(line, defaultport)
- hoststr = ','.join(('0x%02x' % b) for b in host)
- g.write(' {{%s}, %i}' % (hoststr, port))
- g.write('\n};\n')
+ spec = parse_spec(line)
+ 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:
@@ -124,14 +146,13 @@ def main():
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 16-byte IPv6 address and a port.\n')
- g.write(' * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.\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, 'pnSeed6_main', 8333)
+ 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, 'pnSeed6_test', 18333)
+ process_nodes(g, f, 'chainparams_seed_test')
g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n')
if __name__ == '__main__':