Source code for bitcoincash.messages

# Copyright (C) 2012-2015 The python-bitcoinlib developers
#
# This file is part of python-bitcoinlib.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-bitcoinlib, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# LICENSE file.

from __future__ import absolute_import, division, print_function, unicode_literals

import hashlib
import random
import struct
import time

# Py3 compatibility
import sys

if sys.version > '3':
    _bchr = lambda x: bytes([x])
    _bord = lambda x: x[0]
    from io import BytesIO as _BytesIO
else:
    _bchr = chr
    _bord = ord
    from cStringIO import StringIO as _BytesIO

# Bad practice, so we have a __all__ at the end; this should be cleaned up
# later.
from bitcoincash.core import *
from bitcoincash.core.serialize import *
from bitcoincash.net import *
import bitcoincash

MSG_TX = 1
MSG_BLOCK = 2
MSG_FILTERED_BLOCK = 3


[docs]class MsgSerializable(Serializable): def __init__(self, protover=PROTO_VERSION): self.protover = protover
[docs] def msg_ser(self, f): raise NotImplementedError
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): raise NotImplementedError
[docs] def to_bytes(self): f = _BytesIO() self.msg_ser(f) body = f.getvalue() res = bitcoincash.params.NETWORK_MAGIC res += self.command res += b"\x00" * (12 - len(self.command)) res += struct.pack(b"<I", len(body)) # add checksum th = hashlib.sha256(body).digest() h = hashlib.sha256(th).digest() res += h[:4] res += body return res
[docs] @classmethod def from_bytes(cls, b, protover=PROTO_VERSION): f = _BytesIO(b) return MsgSerializable.stream_deserialize(f, protover=protover)
[docs] @classmethod def stream_deserialize(cls, f, protover=PROTO_VERSION): recvbuf = ser_read(f, 4 + 12 + 4 + 4) # check magic if recvbuf[:4] != bitcoincash.params.NETWORK_MAGIC: raise ValueError("Invalid message start '%s', expected '%s'" % (b2x(recvbuf[:4]), b2x(bitcoincash.params.NETWORK_MAGIC))) # remaining header fields: command, msg length, checksum command = recvbuf[4:4+12].split(b"\x00", 1)[0] msglen = struct.unpack(b"<i", recvbuf[4+12:4+12+4])[0] checksum = recvbuf[4+12+4:4+12+4+4] # read message body recvbuf += ser_read(f, msglen) msg = recvbuf[4+12+4+4:4+12+4+4+msglen] th = hashlib.sha256(msg).digest() h = hashlib.sha256(th).digest() if checksum != h[:4]: raise ValueError("got bad checksum %s" % repr(recvbuf)) recvbuf = recvbuf[4+12+4+4+msglen:] if command in messagemap: cls = messagemap[command] # print("Going to deserialize '%s'" % msg) return cls.msg_deser(_BytesIO(msg)) else: print("Command '%s' not in messagemap" % repr(command)) return None
[docs] def stream_serialize(self, f): data = self.to_bytes() f.write(data)
[docs]class msg_version(MsgSerializable): command = b"version" def __init__(self, protover=PROTO_VERSION): super(msg_version, self).__init__(protover) self.nVersion = protover self.nServices = 1 self.nTime = int(time.time()) self.addrTo = CAddress(PROTO_VERSION) self.addrFrom = CAddress(PROTO_VERSION) self.nNonce = random.getrandbits(64) self.strSubVer = (b'/python-bitcoincashlib:' + bitcoincash.__version__.encode('ascii') + b'/') self.nStartingHeight = -1 self.fRelay = True
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.nVersion = struct.unpack(b"<i", ser_read(f, 4))[0] if c.nVersion == 10300: c.nVersion = 300 c.nServices = struct.unpack(b"<Q", ser_read(f, 8))[0] c.nTime = struct.unpack(b"<q", ser_read(f, 8))[0] c.addrTo = CAddress.stream_deserialize(f, True) if c.nVersion >= 106: c.addrFrom = CAddress.stream_deserialize(f, True) c.nNonce = struct.unpack(b"<Q", ser_read(f, 8))[0] c.strSubVer = VarStringSerializer.stream_deserialize(f) if c.nVersion >= 209: c.nStartingHeight = struct.unpack(b"<i", ser_read(f,4))[0] else: c.nStartingHeight = None else: c.addrFrom = None c.nNonce = None c.strSubVer = None c.nStartingHeight = None if c.nVersion >= 70001: c.fRelay = struct.unpack(b"<B", ser_read(f,1))[0] else: c.fRelay = True return c
[docs] def msg_ser(self, f): f.write(struct.pack(b"<i", self.nVersion)) f.write(struct.pack(b"<Q", self.nServices)) f.write(struct.pack(b"<q", self.nTime)) self.addrTo.stream_serialize(f, True) self.addrFrom.stream_serialize(f, True) f.write(struct.pack(b"<Q", self.nNonce)) VarStringSerializer.stream_serialize(self.strSubVer, f) f.write(struct.pack(b"<i", self.nStartingHeight)) f.write(struct.pack(b"<B", self.fRelay))
def __repr__(self): return "msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i fRelay=%r)" % (self.nVersion, self.nServices, time.ctime(self.nTime), repr(self.addrTo), repr(self.addrFrom), self.nNonce, self.strSubVer, self.nStartingHeight, self.fRelay)
[docs]class msg_verack(MsgSerializable): command = b"verack" def __init__(self, protover=PROTO_VERSION): super(msg_verack, self).__init__(protover) self.protover = protover
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): return cls()
[docs] def msg_ser(self, f): f.write(b"")
def __repr__(self): return "msg_verack()"
[docs]class msg_addr(MsgSerializable): command = b"addr" def __init__(self, protover=PROTO_VERSION): super(msg_addr, self).__init__(protover) self.addrs = []
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.addrs = VectorSerializer.stream_deserialize(CAddress, f) return c
[docs] def msg_ser(self, f): VectorSerializer.stream_serialize(CAddress, self.addrs, f)
def __repr__(self): return "msg_addr(addrs=%s)" % (repr(self.addrs))
[docs]class msg_alert(MsgSerializable): command = b"alert" def __init__(self, protover=PROTO_VERSION): super(msg_alert, self).__init__(protover) self.alert = CAlert()
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.alert = CAlert.stream_deserialize(f) return c
[docs] def msg_ser(self, f): self.alert.stream_serialize(f)
def __repr__(self): return "msg_alert(alert=%s)" % (repr(self.alert), )
[docs]class msg_inv(MsgSerializable): command = b"inv" def __init__(self, protover=PROTO_VERSION): super(msg_inv, self).__init__(protover) self.inv = []
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.inv = VectorSerializer.stream_deserialize(CInv, f) return c
[docs] def msg_ser(self, f): VectorSerializer.stream_serialize(CInv, self.inv, f)
def __repr__(self): return "msg_inv(inv=%s)" % (repr(self.inv))
[docs]class msg_getdata(MsgSerializable): command = b"getdata" def __init__(self, protover=PROTO_VERSION): super(msg_getdata, self).__init__(protover) self.inv = []
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.inv = VectorSerializer.stream_deserialize(CInv, f) return c
[docs] def msg_ser(self, f): VectorSerializer.stream_serialize(CInv, self.inv, f)
def __repr__(self): return "msg_getdata(inv=%s)" % (repr(self.inv))
class msg_notfound(MsgSerializable): command = b"notfound" def __init__(self, protover=PROTO_VERSION): super(msg_notfound, self).__init__(protover) self.inv = [] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.inv = VectorSerializer.stream_deserialize(CInv, f) return c def msg_ser(self, f): VectorSerializer.stream_serialize(CInv, self.inv, f) def __repr__(self): return "msg_notfound(inv=%s)" % (repr(self.inv))
[docs]class msg_getblocks(MsgSerializable): command = b"getblocks" def __init__(self, protover=PROTO_VERSION): super(msg_getblocks, self).__init__(protover) self.locator = CBlockLocator() self.hashstop = b'\x00'*32
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.locator = CBlockLocator.stream_deserialize(f) c.hashstop = ser_read(f, 32) return c
[docs] def msg_ser(self, f): self.locator.stream_serialize(f) f.write(self.hashstop)
def __repr__(self): return "msg_getblocks(locator=%s hashstop=%s)" % (repr(self.locator), b2x(self.hashstop))
[docs]class msg_getheaders(MsgSerializable): command = b"getheaders" def __init__(self, protover=PROTO_VERSION): super(msg_getheaders, self).__init__(protover) self.locator = CBlockLocator() self.hashstop = b'\x00'*32
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.locator = CBlockLocator.stream_deserialize(f) c.hashstop = ser_read(f, 32) return c
[docs] def msg_ser(self, f): self.locator.stream_serialize(f) f.write(self.hashstop)
def __repr__(self): return "msg_getheaders(locator=%s hashstop=%s)" % (repr(self.locator), b2x(self.hashstop))
[docs]class msg_headers(MsgSerializable): command = b"headers" def __init__(self, protover=PROTO_VERSION): super(msg_headers, self).__init__(protover) self.headers = []
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.headers = VectorSerializer.stream_deserialize(CBlockHeader, f) return c
[docs] def msg_ser(self, f): VectorSerializer.stream_serialize(CBlockHeader, self.headers, f)
def __repr__(self): return "msg_headers(headers=%s)" % (repr(self.headers))
[docs]class msg_tx(MsgSerializable): command = b"tx" def __init__(self, protover=PROTO_VERSION): super(msg_tx, self).__init__(protover) self.tx = CTransaction()
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.tx = CTransaction.stream_deserialize(f) return c
[docs] def msg_ser(self, f): self.tx.stream_serialize(f)
def __repr__(self): return "msg_tx(tx=%s)" % (repr(self.tx))
[docs]class msg_block(MsgSerializable): command = b"block" def __init__(self, protover=PROTO_VERSION): super(msg_block, self).__init__(protover) self.block = CBlock()
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.block = CBlock.stream_deserialize(f) return c
[docs] def msg_ser(self, f): self.block.stream_serialize(f)
def __repr__(self): return "msg_block(block=%s)" % (repr(self.block))
[docs]class msg_getaddr(MsgSerializable): command = b"getaddr" def __init__(self, protover=PROTO_VERSION): super(msg_getaddr, self).__init__(protover)
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): return cls()
[docs] def msg_ser(self, f): pass
def __repr__(self): return "msg_getaddr()"
[docs]class msg_ping(MsgSerializable): command = b"ping" def __init__(self, protover=PROTO_VERSION, nonce=0): super(msg_ping, self).__init__(protover) self.nonce = nonce
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.nonce = struct.unpack(b"<Q", ser_read(f, 8))[0] return c
[docs] def msg_ser(self, f): f.write(struct.pack(b"<Q", self.nonce))
def __repr__(self): return "msg_ping(0x%x)" % (self.nonce,)
[docs]class msg_pong(MsgSerializable): command = b"pong" def __init__(self, protover=PROTO_VERSION, nonce=0): super(msg_pong, self).__init__(protover) self.nonce = nonce
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.nonce = struct.unpack(b"<Q", ser_read(f,8))[0] return c
[docs] def msg_ser(self, f): f.write(struct.pack(b"<Q", self.nonce))
def __repr__(self): return "msg_pong(0x%x)" % (self.nonce,)
class msg_reject(MsgSerializable): command = b"reject" def __init__(self, protover=PROTO_VERSION): super(msg_reject, self).__init__(protover) self.message = b'(Message Unitialized)' self.ccode = b'\0' self.reason = b'(Reason Unitialized)' @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): c = cls() c.message = VarStringSerializer.stream_deserialize(f) c.ccode = struct.unpack(b"<c", ser_read(f,1))[0] c.reason = VarStringSerializer.stream_deserialize(f) return c def msg_ser(self, f): VarStringSerializer.stream_serialize(self.message, f) f.write(struct.pack(b"<c", self.ccode)) VarStringSerializer.stream_serialize(self.reason, f) def __repr__(self): return "msg_reject(messsage=%s, ccode=%s, reason=%s)" % (self.message, self.ccode, self.reason)
[docs]class msg_mempool(MsgSerializable): command = b"mempool" def __init__(self, protover=PROTO_VERSION): super(msg_mempool, self).__init__(protover)
[docs] @classmethod def msg_deser(cls, f, protover=PROTO_VERSION): return cls()
[docs] def msg_ser(self, f): pass
def __repr__(self): return "msg_mempool()"
msg_classes = [msg_version, msg_verack, msg_addr, msg_alert, msg_inv, msg_getdata, msg_notfound, msg_getblocks, msg_getheaders, msg_headers, msg_tx, msg_block, msg_getaddr, msg_ping, msg_pong, msg_reject, msg_mempool] messagemap = {} for cls in msg_classes: messagemap[cls.command] = cls __all__ = ( 'MSG_TX', 'MSG_BLOCK', 'MSG_FILTERED_BLOCK', 'MsgSerializable', 'msg_version', 'msg_verack', 'msg_addr', 'msg_alert', 'msg_inv', 'msg_getdata', 'msg_getblocks', 'msg_getheaders', 'msg_headers', 'msg_tx', 'msg_block', 'msg_getaddr', 'msg_ping', 'msg_pong', 'msg_mempool', 'msg_classes', 'messagemap', )