Source code for coinbits.protocol.fields

from cStringIO import StringIO
import struct
import socket

#: The protocol version
PROTOCOL_VERSION = 60002

#: The network magic values
MAGIC_VALUES = {
    "bitcoin": 0xD9B4BEF9,
    "bitcoin_testnet": 0xDAB5BFFA,
    "bitcoin_testnet3": 0x0709110B,
    "namecoin": 0xFEB4BEF9,
    "litecoin": 0xDBB6C0FB,
    "litecoin_testnet": 0xDCB7C1FC
}

#: The available services
SERVICES = {
    "NODE_NETWORK": 0x1,
}

#: The type of the inventories
INVENTORY_TYPE = {
    "ERROR": 0,
    "MSG_TX": 1,
    "MSG_BLOCK": 2,
}


[docs]class Field(object): """ Base class for the Fields. This class only implements the counter to keep the order of the fields on the serializer classes. """ counter = 0 def __init__(self): self.count = Field.counter Field.counter += 1
[docs] def parse(self, value): """ This method should be implemented to parse the value parameter into the field internal representation. :param value: value to be parsed """ raise NotImplemented
[docs] def deserialize(self, stream): """ This method must read the stream data and then deserialize and return the deserialized content. :returns: the deserialized content :param stream: stream of data to read """ raise NotImplemented
[docs] def serialize(self): """Serialize the internal representation and return the serialized data. :returns: the serialized data """ raise NotImplemented
def __repr__(self): return "<%s [%r]>" % (self.__class__.__name__, repr(self.value)) def __str__(self): return str(self.value)
[docs]class PrimaryField(Field): """ This is a base class for all fields that has only one value and their value can be represented by a Python struct datatype. Example of use:: class UInt32LEField(PrimaryField): datatype = "<I" """
[docs] def parse(self, value): """This method will set the internal value to the specified value. :param value: the value to be set """ self.value = value
[docs] def deserialize(self, stream): """Deserialize the stream using the struct data type specified. :param stream: the data stream """ data_size = struct.calcsize(self.datatype) data = stream.read(data_size) return struct.unpack(self.datatype, data)[0]
[docs] def serialize(self): """Serialize the internal data and then return the serialized data.""" data = struct.pack(self.datatype, self.value) return data
[docs]class Int32LEField(PrimaryField): """32-bit little-endian integer field.""" datatype = "<i"
[docs]class UInt32LEField(PrimaryField): """32-bit little-endian unsigned integer field.""" datatype = "<I"
[docs]class Int64LEField(PrimaryField): """64-bit little-endian integer field.""" datatype = "<q"
[docs]class UInt64LEField(PrimaryField): """64-bit little-endian unsigned integer field.""" datatype = "<Q"
[docs]class Int16LEField(PrimaryField): """16-bit little-endian integer field.""" datatype = "<h"
[docs]class UInt16LEField(PrimaryField): """16-bit little-endian unsigned integer field.""" datatype = "<H"
[docs]class UInt16BEField(PrimaryField): """16-bit big-endian unsigned integer field.""" datatype = ">H"
[docs]class FixedStringField(Field): """A fixed length string field. Example of use:: class MessageHeaderSerializer(Serializer): model_class = MessageHeader magic = fields.UInt32LEField() command = fields.FixedStringField(12) length = fields.UInt32LEField() checksum = fields.UInt32LEField() """ def __init__(self, length): super(FixedStringField, self).__init__() self.length = length
[docs] def parse(self, value): self.value = value[:self.length]
[docs] def deserialize(self, stream): data = stream.read(self.length) return data.split("\x00", 1)[0]
[docs] def serialize(self): bin_data = StringIO() bin_data.write(self.value[:self.length]) bin_data.write("\x00" * (12 - len(self.value))) return bin_data.getvalue()
[docs]class NestedField(Field): """A field used to nest another serializer. Example of use:: class TxInSerializer(Serializer): model_class = TxIn previous_output = fields.NestedField(OutPointSerializer) signature_script = fields.VariableStringField() sequence = fields.UInt32LEField() """ def __init__(self, serializer_class): super(NestedField, self).__init__() self.serializer_class = serializer_class self.serializer = self.serializer_class()
[docs] def parse(self, value): self.value = value
[docs] def deserialize(self, stream): return self.serializer.deserialize(stream)
[docs] def serialize(self): return self.serializer.serialize(self.value)
[docs]class ListField(Field): """A field used to serialize/deserialize a list of serializers. Example of use:: class TxSerializer(Serializer): model_class = Tx version = fields.UInt32LEField() tx_in = fields.ListField(TxInSerializer) tx_out = fields.ListField(TxOutSerializer) lock_time = fields.UInt32LEField() """ def __init__(self, serializer_class): super(ListField, self).__init__() self.serializer_class = serializer_class self.var_int = VariableIntegerField()
[docs] def parse(self, value): self.value = value
[docs] def serialize(self): bin_data = StringIO() self.var_int.parse(len(self)) bin_data.write(self.var_int.serialize()) serializer = self.serializer_class() for item in self: bin_data.write(serializer.serialize(item)) return bin_data.getvalue()
[docs] def deserialize(self, stream): count = self.var_int.deserialize(stream) items = [] serializer = self.serializer_class() for i in xrange(count): data = serializer.deserialize(stream) items.append(data) return items
def __iter__(self): return iter(self.value) def __len__(self): return len(self.value)
[docs]class IPv4AddressField(Field): """An IPv4 address field without timestamp and reserved IPv6 space.""" reserved = "\x00" * 10 + "\xff" * 2
[docs] def parse(self, value): self.value = value
[docs] def deserialize(self, stream): # unused, reserved bytes stream.read(12) addr = stream.read(4) return socket.inet_ntoa(addr)
[docs] def serialize(self): bin_data = StringIO() bin_data.write(self.reserved) bin_data.write(socket.inet_aton(self.value)) return bin_data.getvalue()
[docs]class VariableIntegerField(Field): """A variable size integer field."""
[docs] def parse(self, value): self.value = int(value)
[docs] def deserialize(self, stream): int_id_raw = stream.read(struct.calcsize("<B")) int_id = struct.unpack("<B", int_id_raw)[0] if int_id == 0xFD: data = stream.read(2) int_id = struct.unpack("<H", data)[0] elif int_id == 0xFE: data = stream.read(4) int_id = struct.unpack("<I", data)[0] elif int_id == 0xFF: data = stream.read(8) int_id = struct.unpack("<Q", data)[0] return int_id
[docs] def serialize(self): if self.value < 0xFD: return chr(self.value) if self.value <= 0xFFFF: return chr(0xFD) + struct.pack("<H", self.value) if self.value <= 0xFFFFFFFF: return chr(0xFE) + struct.pack("<I", self.value) return chr(0xFF) + struct.pack("<Q", self.value)
[docs]class VariableStringField(Field): """A variable length string field.""" def __init__(self): super(VariableStringField, self).__init__() self.var_int = VariableIntegerField()
[docs] def parse(self, value): self.value = str(value)
[docs] def deserialize(self, stream): string_length = self.var_int.deserialize(stream) string_data = stream.read(string_length) return string_data
[docs] def serialize(self): self.var_int.parse(len(self)) bin_data = StringIO() bin_data.write(self.var_int.serialize()) bin_data.write(self.value) return bin_data.getvalue()
def __len__(self): return len(self.value)
[docs]class Hash(Field): """A hash type field.""" datatype = "<I"
[docs] def parse(self, value): self.value = value
[docs] def deserialize(self, stream): data_size = struct.calcsize(self.datatype) intvalue = 0 for i in range(8): data = stream.read(data_size) val = struct.unpack(self.datatype, data)[0] intvalue += val << (i * 32) return intvalue
[docs] def serialize(self): hash_ = self.value bin_data = StringIO() for i in range(8): pack_data = struct.pack(self.datatype, hash_ & 0xFFFFFFFF) bin_data.write(pack_data) hash_ >>= 32 return bin_data.getvalue()
[docs]class BlockLocator(Field): """A block locator type used for getblocks and getheaders""" datatype = "<I"
[docs] def parse(self, values): self.values = values
[docs] def serialize(self): bin_data = StringIO() for hash_ in self.values: for i in range(8): pack_data = struct.pack(self.datatype, hash_ & 0xFFFFFFFF) bin_data.write(pack_data) hash_ >>= 32 return bin_data.getvalue()