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.
251use obj.serialize().hex()"""252obj.deserialize(BytesIO(bytes.fromhex(hex_string)))253return obj256def tx_from_hex(hex_string):257"""Deserialize from hex string to a transaction object"""258return from_hex(CTransaction(), hex_string)261def malleate_tx_to_invalid_witness(tx):262"""263Create a malleated version of the tx where the witness is replaced with garbage data.264Returns a CTransaction object.265"""266tx_bad_wit = tx_from_hex(tx["hex"])267tx_bad_wit.wit.vtxinwit = [CTxInWitness()]268# Add garbage data to witness 0. We cannot simply strip the witness, as the node would269# classify it as a transaction in which the witness was missing rather than wrong.270tx_bad_wit.wit.vtxinwit[0].scriptWitness.stack = [b'garbage']272assert_equal(tx["txid"], tx_bad_wit.txid_hex)273assert_not_equal(tx["wtxid"], tx_bad_wit.wtxid_hex)275return tx_bad_wit278# like from_hex, but without the hex part279def from_binary(cls, stream):280"""deserialize a binary stream (or bytes object) into an object"""281# handle bytes object by turning it into a stream282was_bytes = isinstance(stream, bytes)283if was_bytes:284stream = BytesIO(stream)285obj = cls()286obj.deserialize(stream)287if was_bytes:288assert_equal(len(stream.read()), 0)289return obj292# Objects that map to bitcoind objects, which can be serialized/deserialized295class CAddress:296__slots__ = ("net", "ip", "nServices", "port", "time")298# see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki299NET_IPV4 = 1300NET_IPV6 = 2301NET_TORV3 = 4302NET_I2P = 5303NET_CJDNS = 6305ADDRV2_NET_NAME = {306NET_IPV4: "IPv4",307NET_IPV6: "IPv6",308NET_TORV3: "TorV3",309NET_I2P: "I2P",310NET_CJDNS: "CJDNS"311}313ADDRV2_ADDRESS_LENGTH = {314NET_IPV4: 4,315NET_IPV6: 16,316NET_TORV3: 32,317NET_I2P: 32,318NET_CJDNS: 16319}321I2P_PAD = "===="323def __init__(self):324self.time = 0325self.nServices = 1326self.net = self.NET_IPV4327self.ip = "0.0.0.0"328self.port = 0330def __eq__(self, other):331return self.net == other.net and self.ip == other.ip and self.nServices == other.nServices and self.port == other.port and self.time == other.time333def deserialize(self, f, *, with_time=True):334"""Deserialize from addrv1 format (pre-BIP155)"""335if with_time:336# VERSION messages serialize CAddress objects without time337self.time = int.from_bytes(f.read(4), "little")338self.nServices = int.from_bytes(f.read(8), "little")339# We only support IPv4 which means skip 12 bytes and read the next 4 as IPv4 address.340f.read(12)341self.net = self.NET_IPV4342self.ip = socket.inet_ntoa(f.read(4))343self.port = int.from_bytes(f.read(2), "big")345def serialize(self, *, with_time=True):346"""Serialize in addrv1 format (pre-BIP155)"""347assert_equal(self.net, self.NET_IPV4)348r = b""349if with_time:350# VERSION messages serialize CAddress objects without time351r += self.time.to_bytes(4, "little")352r += self.nServices.to_bytes(8, "little")353r += b"\x00" * 10 + b"\xff" * 2354r += socket.inet_aton(self.ip)355r += self.port.to_bytes(2, "big")356return r358def deserialize_v2(self, f):359"""Deserialize from addrv2 format (BIP155)"""360self.time = int.from_bytes(f.read(4), "little")362self.nServices = deser_compact_size(f)364self.net = int.from_bytes(f.read(1), "little")365assert self.net in self.ADDRV2_NET_NAME367address_length = deser_compact_size(f)368assert_equal(address_length, self.ADDRV2_ADDRESS_LENGTH[self.net])370addr_bytes = f.read(address_length)371if self.net == self.NET_IPV4:372self.ip = socket.inet_ntoa(addr_bytes)373elif self.net == self.NET_IPV6:374self.ip = socket.inet_ntop(socket.AF_INET6, addr_bytes)375elif self.net == self.NET_TORV3:376prefix = b".onion checksum"377version = bytes([3])378checksum = sha3(prefix + addr_bytes + version)[:2]379self.ip = b32encode(addr_bytes + checksum + version).decode("ascii").lower() + ".onion"380elif self.net == self.NET_I2P:381self.ip = b32encode(addr_bytes)[0:-len(self.I2P_PAD)].decode("ascii").lower() + ".b32.i2p"382elif self.net == self.NET_CJDNS:383self.ip = socket.inet_ntop(socket.AF_INET6, addr_bytes)384else:385raise Exception("Address type not supported")387self.port = int.from_bytes(f.read(2), "big")389def serialize_v2(self):390"""Serialize in addrv2 format (BIP155)"""391assert self.net in self.ADDRV2_NET_NAME392r = b""393r += self.time.to_bytes(4, "little")394r += ser_compact_size(self.nServices)395r += self.net.to_bytes(1, "little")396r += ser_compact_size(self.ADDRV2_ADDRESS_LENGTH[self.net])397if self.net == self.NET_IPV4:398r += socket.inet_aton(self.ip)399elif self.net == self.NET_IPV6:400r += socket.inet_pton(socket.AF_INET6, self.ip)401elif self.net == self.NET_TORV3:402sfx = ".onion"403assert self.ip.endswith(sfx)404r += b32decode(self.ip[0:-len(sfx)], True)[0:32]405elif self.net == self.NET_I2P:406sfx = ".b32.i2p"407assert self.ip.endswith(sfx)408r += b32decode(self.ip[0:-len(sfx)] + self.I2P_PAD, True)409elif self.net == self.NET_CJDNS:410r += socket.inet_pton(socket.AF_INET6, self.ip)411else:412raise Exception("Address type not supported")413r += self.port.to_bytes(2, "big")414return r416def __repr__(self):417return ("CAddress(nServices=%i net=%s addr=%s port=%i)"418% (self.nServices, self.ADDRV2_NET_NAME[self.net], self.ip, self.port))421class CInv:422__slots__ = ("hash", "type")424typemap = {4250: "Error",426MSG_TX: "TX",427MSG_BLOCK: "Block",428MSG_TX | MSG_WITNESS_FLAG: "WitnessTx",429MSG_BLOCK | MSG_WITNESS_FLAG: "WitnessBlock",430MSG_FILTERED_BLOCK: "filtered Block",431MSG_CMPCT_BLOCK: "CompactBlock",432MSG_WTX: "WTX",433}435def __init__(self, t=0, h=0):436self.type = t437self.hash = h439def deserialize(self, f):440self.type = int.from_bytes(f.read(4), "little")441self.hash = deser_uint256(f)443def serialize(self):444r = b""445r += self.type.to_bytes(4, "little")446r += ser_uint256(self.hash)447return r449def __repr__(self):450return "CInv(type=%s hash=%064x)" \451% (self.typemap[self.type], self.hash)453def __eq__(self, other):454return isinstance(other, CInv) and self.hash == other.hash and self.type == other.type457class CBlockLocator:458__slots__ = ("nVersion", "vHave")460def __init__(self):461self.vHave = []463def deserialize(self, f):464int.from_bytes(f.read(4), "little", signed=True) # Ignore version field.465self.vHave = deser_uint256_vector(f)467def serialize(self):468r = b""469r += (0).to_bytes(4, "little", signed=True) # Bitcoin Core ignores the version field. Set it to 0.470r += ser_uint256_vector(self.vHave)471return r473def __repr__(self):474return "CBlockLocator(vHave=%s)" % (repr(self.vHave))477class COutPoint:478__slots__ = ("hash", "n")480def __init__(self, hash=0, n=0):481self.hash = hash482self.n = n484def deserialize(self, f):485self.hash = deser_uint256(f)486self.n = int.from_bytes(f.read(4), "little")488def serialize(self):489r = b""490r += ser_uint256(self.hash)491r += self.n.to_bytes(4, "little")492return r494def __repr__(self):495return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n)498class CTxIn:499__slots__ = ("nSequence", "prevout", "scriptSig")
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.