Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2014-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5'''6Script to generate list of seed nodes for kernel/chainparams.cpp.8This script expects three text files in the directory that is passed as an9argument:11nodes_main.txt12nodes_signet.txt13nodes_test.txt14nodes_testnet4.txt16These files must consist of lines in the format18<ip>:<port>19[<ipv6>]:<port>20<onion>.onion:<port>21<i2p>.b32.i2p:<port>23The output will be several data structures with the peers in binary format:25inline constexpr uint8_t chainparams_seed_{main,signet,test,testnet4}[]{26...27};29These should be pasted into `src/chainparamsseeds.h`.30'''32from base64 import b32decode33from enum import Enum34import sys35import os36import re38class BIP155Network(Enum):39IPV4 = 140IPV6 = 241TORV2 = 3 # no longer supported42TORV3 = 443I2P = 544CJDNS = 646def name_to_bip155(addr):47'''Convert address string to BIP155 (networkID, addr) tuple.'''48if addr.endswith('.onion'):49vchAddr = b32decode(addr[0:-6], True)50if len(vchAddr) == 35:51assert vchAddr[34] == 352return (BIP155Network.TORV3, vchAddr[:32])53elif len(vchAddr) == 10:54return (BIP155Network.TORV2, vchAddr)55else:56raise ValueError('Invalid onion %s' % vchAddr)57elif addr.endswith('.b32.i2p'):58vchAddr = b32decode(addr[0:-8] + '====', True)59if len(vchAddr) == 32:60return (BIP155Network.I2P, vchAddr)61else:62raise ValueError(f'Invalid I2P {vchAddr}')63elif '.' in addr: # IPv464return (BIP155Network.IPV4, bytes((int(x) for x in addr.split('.'))))65elif ':' in addr: # IPv6 or CJDNS66sub = [[], []] # prefix, suffix67x = 068addr = addr.split(':')69for i,comp in enumerate(addr):70if comp == '':71if i == 0 or i == (len(addr)-1): # skip empty component at beginning or end72continue73x += 1 # :: skips to suffix74assert x < 275else: # two bytes per component76val = int(comp, 16)77sub[x].append(val >> 8)78sub[x].append(val & 0xff)79nullbytes = 16 - len(sub[0]) - len(sub[1])80assert (x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0)81addr_bytes = bytes(sub[0] + ([0] * nullbytes) + sub[1])82if addr_bytes[0] == 0xfc:83# Assume that seeds with fc00::/8 addresses belong to CJDNS,84# not to the publicly unroutable "Unique Local Unicast" network, see85# RFC4193: https://datatracker.ietf.org/doc/html/rfc4193#section-886return (BIP155Network.CJDNS, addr_bytes)87else:88return (BIP155Network.IPV6, addr_bytes)89else:90raise ValueError('Could not parse address %s' % addr)92def parse_spec(s):93'''Convert endpoint string to BIP155 (networkID, addr, port) tuple.'''94match = re.match(r'\[([0-9a-fA-F:]+)\](?::([0-9]+))?$', s)95if match: # ipv696host = match.group(1)97port = match.group(2)98elif s.count(':') > 1: # ipv6, no port99host = s100port = ''101else:102(host,_,port) = s.partition(':')104if not port:105port = 0106else:107port = int(port)109host = name_to_bip155(host)111if host[0] == BIP155Network.TORV2:112return None # TORV2 is no longer supported, so we ignore it113else:114return host + (port, )116def ser_compact_size(l):117r = b""118if l < 253:119r = l.to_bytes(1, "little")120elif l < 0x10000:121r = (253).to_bytes(1, "little") + l.to_bytes(2, "little")122elif l < 0x100000000:123r = (254).to_bytes(1, "little") + l.to_bytes(4, "little")124else:125r = (255).to_bytes(1, "little") + l.to_bytes(8, "little")126return r128def bip155_serialize(spec):129'''130Serialize (networkID, addr, port) tuple to BIP155 binary format.131'''132r = b""133r += spec[0].value.to_bytes(1, "little")134r += ser_compact_size(len(spec[1]))135r += spec[1]136r += spec[2].to_bytes(2, "big")137return r139def process_nodes(g, f, structname):140g.write("inline constexpr uint8_t %s[]{\n" % structname)141for line in f:142comment = line.find('#')143if comment != -1:144line = line[0:comment]145line = line.strip()146if not line:147continue149spec = parse_spec(line)150if spec is None: # ignore this entry (e.g. no longer supported addresses like TORV2)151continue152blob = bip155_serialize(spec)153hoststr = ','.join(('0x%02x' % b) for b in blob)154g.write(f' {hoststr},\n')155g.write('};\n')157def main():158if len(sys.argv)<2:159print(('Usage: %s <path_to_nodes_txt>' % sys.argv[0]), file=sys.stderr)160sys.exit(1)161g = sys.stdout162indir = sys.argv[1]163g.write('// Copyright (c) The Bitcoin Core developers\n')164g.write('// Distributed under the MIT software license, see the accompanying\n')165g.write('// file COPYING or https://opensource.org/license/mit.\n\n')166g.write('#ifndef BITCOIN_CHAINPARAMSSEEDS_H\n')167g.write('#define BITCOIN_CHAINPARAMSSEEDS_H\n')168g.write('/**\n')169g.write(' * List of fixed seed nodes for the bitcoin network\n')170g.write(' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n')171g.write(' *\n')172g.write(' * Each line contains a BIP155 serialized (networkID, addr, port) tuple.\n')173g.write(' */\n')174with open(os.path.join(indir,'nodes_main.txt'), 'r') as f:175process_nodes(g, f, 'chainparams_seed_main')176g.write('\n')177with open(os.path.join(indir,'nodes_signet.txt'), 'r') as f:178process_nodes(g, f, 'chainparams_seed_signet')179g.write('\n')180with open(os.path.join(indir,'nodes_test.txt'), 'r') as f:181process_nodes(g, f, 'chainparams_seed_test')182g.write('\n')183with open(os.path.join(indir,'nodes_testnet4.txt'), 'r') as f:184process_nodes(g, f, 'chainparams_seed_testnet4')185g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n')187if __name__ == '__main__':188main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.