Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import struct
- import hashlib
- """All those classes are used to convert python objects to the binary bitcoin
- format and back.
- Every class has two methods:
- -serialize:
- converts a value to the bitcoin format, returning it as a string
- -deserialize:
- converts from a binary bitcoin format (starting from an optional offset) to an
- usable python object.
- Returns a tuple of (value, size)
- Every class also has an attribute, called default, that contains a callable that
- will produce the default python value to be used.
- """
- class NotEnoughDataException(Exception):
- """Exception thrown when we try to deserialize something but haven't gotten
- enough bytes read to do it."""
- pass
- class UInt8(object):
- """UInt8 converts python integers to and from little endian 8 bit encoded
- integers."""
- default = int
- @staticmethod
- def serialize(inp):
- return struct.pack("<B", inp)
- @staticmethod
- def deserialize(bytes, offset = 0):
- if len(bytes) - offset < 1:
- raise NotEnoughDataException()
- return (struct.unpack("<B", bytes[offset:offset+1])[0], 1)
- class UInt16(object):
- """Handles the conversions between integers and unsigned 16bit integers
- used by bitcoin (little endian)."""
- default = int
- @staticmethod
- def serialize(inp):
- return struct.pack("<H", inp)
- @staticmethod
- def deserialize(bytes, offset = 0):
- if len(bytes) - offset < 2:
- raise NotEnoughDataException()
- return (struct.unpack("<H", bytes[offset:offset+2])[0], 2)
- class UInt16BigEndian(object):
- """Handles the conversions between integers and unsigned 16bit integers
- used by bitcoin (big endian)."""
- default = int
- @staticmethod
- def serialize(inp):
- return struct.pack(">H", inp)
- @staticmethod
- def deserialize(bytes, offset = 0):
- if len(bytes) - offset < 2:
- raise NotEnoughDataException()
- return (struct.unpack(">H", bytes[offset:offset+2])[0], 2)
- class UInt32(object):
- """Handles the conversions between integers and unsigned 32bit integers
- used by bitcoin (little endian)."""
- default = int
- @staticmethod
- def serialize(inp):
- return struct.pack("<I", inp)
- @staticmethod
- def deserialize(bytes, offset = 0):
- if len(bytes) - offset < 4:
- raise NotEnoughDataException()
- return (struct.unpack("<I", bytes[offset:offset+4])[0], 4)
- class UInt64(object):
- """Handles the conversions between integers and unsigned 64bit integers
- used by bitcoin (little endian)."""
- default = int
- @staticmethod
- def serialize(inp):
- return struct.pack("<Q", inp)
- @staticmethod
- def deserialize(bytes, offset = 0):
- if len(bytes) - offset < 8:
- raise NotEnoughDataException()
- return (struct.unpack("<Q", bytes[offset:offset+8])[0], 8)
- class VarInt(object):
- """Implements the variable integers used by bitcoin, which can vary in size
- depending on how big the integer value is.
- """
- default = int
- @staticmethod
- def serialize(inp):
- if inp < 0xFD:
- return struct.pack("<B", inp)
- elif inp <= 0xFFFF:
- return "\xfd" + struct.pack("<H", inp)
- elif inp <= 0xffffffff:
- return "\xfe" + struct.pack("<I", inp)
- else:
- return "\xff" + struct.pack("<Q", inp)
- @staticmethod
- def deserialize(bytes, offset = 0):
- if bytes[offset] == "\xff":
- if len(bytes) - offset < 9:
- raise NotEnoughDataException()
- return (struct.unpack("<Q", bytes[offset+1:offset+9])[0], 9)
- elif bytes[offset] == "\xfe":
- if len(bytes) - offset < 5:
- raise NotEnoughDataException()
- return (struct.unpack("<I", bytes[offset+1:offset+5])[0], 5)
- elif bytes[offset] == "\xfd":
- if len(bytes) - offset < 3:
- raise NotEnoughDataException()
- return (struct.unpack("<H", bytes[offset+1:offset+3])[0], 3)
- else:
- if len(bytes) - offset < 1:
- raise NotEnoughDataException()
- return (struct.unpack("<B", bytes[offset:offset+1])[0], 1)
- class VarString(object):
- """Implementation of a variable length string: the size of the string is
- stored right in front of it."""
- default = str
- @staticmethod
- def serialize(inp):
- length = VarInt.serialize(len(inp))
- outp = length + inp
- return outp
- @staticmethod
- def deserialize(bytes, offset = 0):
- (lengthofstring, size) = VarInt.deserialize(bytes, offset)
- if len(bytes) - offset < lengthofstring + size:
- raise NotEnoughDataException()
- outp = bytes[offset + size:offset + size + lengthofstring]
- return (outp, size + lengthofstring)
- class FixedString(object):
- """Implemented fixed length strings: those strings al are stringsize big,
- padded with null bytes at the end to fill up."""
- default = str
- def __init__(self, stringsize):
- """The stringsize argument defines how big our fixed length string
- should be."""
- self.size = stringsize
- def serialize(self, inp):
- return inp[:self.size].ljust(self.size, "\x00")
- def deserialize(self, bytes, offset = 0):
- if len(bytes) - offset < self.size:
- raise NotEnoughDataException()
- return (bytes[offset:offset+self.size].rstrip("\x00"), self.size)
- class Array(object):
- """Implements arrays for structures. The number of items is written in front
- with a VarInt, followed by every item."""
- default = list
- def __init__(self, structure):
- self.structure = structure
- def serialize(self, inp):
- output = VarInt.serialize(len(inp))
- for x in inp:
- serialized = self.structure.serialize(x)
- output += serialized
- return output
- def deserialize(self, bytes, offset = 0):
- (count, countsize) = VarInt.deserialize(bytes, offset)
- totalsize = countsize
- elements = []
- for x in xrange(0, count):
- (element, elementsize) = self.structure.deserialize(bytes,
- offset + totalsize)
- totalsize += elementsize
- elements.append(element)
- return (elements, totalsize)
- class Structure(object):
- """This class is to be used as a base for structures, which is just a fancy
- way to call a collection of other structures and base types really.
- Every structure needs to have a fields attribute: a list of (fieldname,
- fieldtype) tuples."""
- fields = []
- @classmethod
- def default(kls):
- return kls()
- def __init__(self):
- """Constructor."""
- for (fieldname, fieldtype) in self.fields:
- setattr(self, fieldname, fieldtype.default())
- def serialize(self):
- """Serializes this structure and returns it as a string."""
- output = ""
- for (fieldname, fieldtype) in self.fields:
- value = getattr(self, fieldname)
- encoded = fieldtype.serialize(value)
- output += encoded
- return output
- @classmethod
- def deserialize(kls, bytes, offset = 0):
- """Deserialize the structure stored in bytes, starting at offset"""
- structure = kls()
- totalsize = 0
- for (fieldname, fieldtype) in kls.fields:
- (value, size) = fieldtype.deserialize(bytes, offset)
- setattr(structure, fieldname, value)
- offset += size
- totalsize += size
- return (structure, totalsize)
- def __repr__(self):
- return self.__class__.__name__ + " (" + \
- ", ".join([fieldname + ": " + repr(getattr(self, fieldname)) \
- for (fieldname, fieldtype) in self.fields]) + \
- ")"
- #Those are the various structures used in the bitcoin protocol
- #Taken from: https://en.bitcoin.it/wiki/Protocol_specification
- #with some changes to make them more easily manageable.
- class NetworkAddress(Structure):
- fields = [
- ("services", UInt64),
- ("ipaddress", FixedString(16)),
- ("port", UInt16BigEndian)
- ]
- class TimestampedNetworkAddress(Structure):
- fields = [
- ("timestamp", UInt32),
- ("services", UInt64),
- ("ipaddress", FixedString(16)),
- ("port", UInt16BigEndian)
- ]
- class MessageHeader(Structure):
- fields = [
- ("magic", FixedString(4)),
- ("command", FixedString(12)),
- ("length", UInt32)
- ]
- class Version(Structure):
- fields = [
- ("version", UInt32),
- ("services", UInt64),
- ("timestamp", UInt64),
- ("addrme", NetworkAddress),
- ("addryou", NetworkAddress),
- ("nonce", UInt64),
- ("subversionumber", VarString),
- ("startheight", UInt32)
- ]
- class Verack(Structure):
- fields = [
- ]
- class Getblocks(Structure):
- fields = [
- ("version", UInt32),
- ("hashstart", Array(FixedString(32))),
- ("hashstop", FixedString(32))
- ]
- class Getheaders(Structure):
- fields = [
- ("verion", UInt32),
- ("hashstart", Array(FixedString(32))),
- ("hashstop", FixedString(32))
- ]
- class BlockHeader(Structure):
- fields = [
- ("version", UInt32),
- ("prevblock", FixedString(32)),
- ("merkleroot", FixedString(32)),
- ("timestamp", UInt32),
- ("difficulty", UInt32),
- ("nonce", UInt32),
- ("txcount", UInt8)
- ]
- def calc_hash(self):
- serialized = self.serialize()[:80]
- h = hashlib.sha256(serialized).digest()
- h = hashlib.sha256(h).digest()
- return h
- class BlockHeaders(Structure):
- fields = [
- ("headers", Array(BlockHeader))
- ]
- class InvVector(Structure):
- fields = [
- ("type", UInt32),
- ("hash", FixedString(32))
- ]
- class Inventory(Structure):
- fields = [
- ("inventory", Array(InvVector)),
- ]
- class GetData(Structure):
- fields = [
- ("inventory", Array(InvVector)),
- ]
- class OutPoint(Structure):
- fields = [
- ("hash", FixedString(32)),
- ("index", UInt32)
- ]
- class TxIn(Structure):
- fields = [
- ("previousoutput", OutPoint),
- ("script", VarString),
- ("sequence", UInt32)
- ]
- class TxOut(Structure):
- fields = [
- ("value", UInt64),
- ("script", VarString)
- ]
- class Tx(Structure):
- fields = [
- ("version", UInt32),
- ("txins", Array(TxIn)),
- ("txout", Array(TxOut)),
- ("locktime", UInt32)
- ]
- def calc_hash(self):
- serialized = self.serialize()
- h = hashlib.sha256(serialized).digest()
- h = hashlib.sha256(h).digest()
- return h
- class Block(Structure):
- fields = [
- ("version", UInt32),
- ("prevblock", FixedString(32)),
- ("merkleroot", FixedString(32)),
- ("timestamp", UInt32),
- ("difficulty", UInt32),
- ("nonce", UInt32),
- ("txns", Array(Tx))
- ]
- def calc_hash(self):
- serialized = self.serialize()[:80]
- h = hashlib.sha256(serialized).digest()
- h = hashlib.sha256(h).digest()
- return h
- class GetAddr(Structure):
- fields = [
- ]
- class Address(Structure):
- fields = [
- ("addresses", Array(TimestampedNetworkAddress))
- ]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement