git / moonbite-core / contrib/seeds/generate-seeds.py · raw

#!/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 anargument:    nodes_main.txt    nodes_test.txtThese 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 b32decodefrom enum import Enumimport structimport sysimport osimport reclass BIP155Network(Enum):    IPV4 = 1    IPV6 = 2    TORV2 = 3  # no longer supported    TORV3 = 4    I2P = 5    CJDNS = 6def 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 rdef 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 rdef 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()