diff options
author | W. J. van der Laan <laanwj@protonmail.com> | 2021-03-31 13:29:21 +0200 |
---|---|---|
committer | W. J. van der Laan <laanwj@protonmail.com> | 2021-04-05 13:59:40 +0200 |
commit | 06030f7a42dea33c5120504dcd99d1714883f271 (patch) | |
tree | 06b969f0698f538ae994bd4e6376d0b15bc644c4 /contrib | |
parent | f9e86d89660002d3dd17211d8b030a47b95d2fa2 (diff) |
contrib: generate-seeds.py generates output in BIP155 format
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/seeds/generate-seeds.py | 103 |
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__': |