Advertisement
Guest User

Untitled

a guest
Jun 16th, 2011
192
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.74 KB | None | 0 0
  1. import struct
  2. import hashlib
  3.  
  4. """All those classes are used to convert python objects to the binary bitcoin
  5. format and back.
  6.  
  7. Every class has two methods:
  8. -serialize:
  9. converts a value to the bitcoin format, returning it as a string
  10.  
  11. -deserialize:
  12. converts from a binary bitcoin format (starting from an optional offset) to an
  13. usable python object.
  14. Returns a tuple of (value, size)
  15.  
  16. Every class also has an attribute, called default, that contains a callable that
  17. will produce the default python value to be used.
  18. """
  19.  
  20. class NotEnoughDataException(Exception):
  21.     """Exception thrown when we try to deserialize something but haven't gotten
  22.    enough bytes read to do it."""
  23.     pass
  24.    
  25. class UInt8(object):
  26.     """UInt8 converts python integers to and from little endian 8 bit encoded
  27.    integers."""
  28.    
  29.     default = int
  30.    
  31.     @staticmethod
  32.     def serialize(inp):
  33.         return struct.pack("<B", inp)
  34.    
  35.     @staticmethod
  36.     def deserialize(bytes, offset = 0):
  37.         if len(bytes) - offset < 1:
  38.             raise NotEnoughDataException()
  39.         return (struct.unpack("<B", bytes[offset:offset+1])[0], 1)
  40.        
  41. class UInt16(object):
  42.     """Handles the conversions between integers and unsigned 16bit integers
  43.    used by bitcoin (little endian)."""
  44.    
  45.     default = int
  46.    
  47.     @staticmethod
  48.     def serialize(inp):
  49.         return struct.pack("<H", inp)
  50.    
  51.     @staticmethod
  52.     def deserialize(bytes, offset = 0):
  53.         if len(bytes) - offset < 2:
  54.             raise NotEnoughDataException()
  55.         return (struct.unpack("<H", bytes[offset:offset+2])[0], 2)
  56.        
  57. class UInt16BigEndian(object):
  58.     """Handles the conversions between integers and unsigned 16bit integers
  59.    used by bitcoin (big endian)."""
  60.    
  61.     default = int
  62.    
  63.     @staticmethod
  64.     def serialize(inp):
  65.         return struct.pack(">H", inp)
  66.    
  67.     @staticmethod
  68.     def deserialize(bytes, offset = 0):
  69.         if len(bytes) - offset < 2:
  70.             raise NotEnoughDataException()
  71.         return (struct.unpack(">H", bytes[offset:offset+2])[0], 2)
  72.        
  73. class UInt32(object):
  74.     """Handles the conversions between integers and unsigned 32bit integers
  75.    used by bitcoin (little endian)."""
  76.    
  77.     default = int
  78.    
  79.     @staticmethod
  80.     def serialize(inp):
  81.         return struct.pack("<I", inp)
  82.    
  83.     @staticmethod
  84.     def deserialize(bytes, offset = 0):
  85.         if len(bytes) - offset < 4:
  86.             raise NotEnoughDataException()
  87.         return (struct.unpack("<I", bytes[offset:offset+4])[0], 4)
  88.  
  89. class UInt64(object):
  90.     """Handles the conversions between integers and unsigned 64bit integers
  91.    used by bitcoin (little endian)."""
  92.    
  93.     default = int
  94.    
  95.     @staticmethod
  96.     def serialize(inp):
  97.         return struct.pack("<Q", inp)
  98.    
  99.     @staticmethod
  100.     def deserialize(bytes, offset = 0):
  101.         if len(bytes) - offset < 8:
  102.             raise NotEnoughDataException()
  103.         return (struct.unpack("<Q", bytes[offset:offset+8])[0], 8)
  104.  
  105. class VarInt(object):
  106.     """Implements the variable integers used by bitcoin, which can vary in size
  107.    depending on how big the integer value is.
  108.    """
  109.    
  110.     default = int
  111.    
  112.     @staticmethod
  113.     def serialize(inp):
  114.         if inp < 0xFD:
  115.             return struct.pack("<B", inp)
  116.         elif inp <= 0xFFFF:
  117.             return "\xfd" + struct.pack("<H", inp)
  118.         elif inp <= 0xffffffff:
  119.             return "\xfe" + struct.pack("<I", inp)
  120.         else:
  121.             return "\xff" + struct.pack("<Q", inp)
  122.  
  123.     @staticmethod
  124.     def deserialize(bytes, offset = 0):
  125.         if bytes[offset] == "\xff":
  126.             if len(bytes) - offset < 9:
  127.                 raise NotEnoughDataException()
  128.             return (struct.unpack("<Q", bytes[offset+1:offset+9])[0], 9)
  129.         elif bytes[offset] == "\xfe":
  130.             if len(bytes) - offset < 5:
  131.                 raise NotEnoughDataException()
  132.             return (struct.unpack("<I", bytes[offset+1:offset+5])[0], 5)
  133.         elif bytes[offset] == "\xfd":
  134.             if len(bytes) - offset < 3:
  135.                 raise NotEnoughDataException()
  136.             return (struct.unpack("<H", bytes[offset+1:offset+3])[0], 3)
  137.         else:
  138.             if len(bytes) - offset < 1:
  139.                 raise NotEnoughDataException()
  140.             return (struct.unpack("<B", bytes[offset:offset+1])[0], 1)
  141.  
  142. class VarString(object):
  143.     """Implementation of a variable length string: the size of the string is
  144.    stored right in front of it."""
  145.    
  146.     default = str
  147.    
  148.     @staticmethod
  149.     def serialize(inp):
  150.         length = VarInt.serialize(len(inp))
  151.         outp = length + inp
  152.         return outp
  153.  
  154.     @staticmethod
  155.     def deserialize(bytes, offset = 0):
  156.         (lengthofstring, size) = VarInt.deserialize(bytes, offset)
  157.         if len(bytes) - offset < lengthofstring + size:
  158.             raise NotEnoughDataException()
  159.         outp = bytes[offset + size:offset + size + lengthofstring]
  160.         return (outp, size + lengthofstring)
  161.        
  162. class FixedString(object):
  163.     """Implemented fixed length strings: those strings al are stringsize big,
  164.    padded with null bytes at the end to fill up."""
  165.    
  166.     default = str
  167.    
  168.     def __init__(self, stringsize):
  169.         """The stringsize argument defines how big our fixed length string
  170.        should be."""
  171.         self.size = stringsize
  172.        
  173.     def serialize(self, inp):
  174.         return inp[:self.size].ljust(self.size, "\x00")
  175.    
  176.     def deserialize(self, bytes, offset = 0):
  177.         if len(bytes) - offset < self.size:
  178.             raise NotEnoughDataException()
  179.         return (bytes[offset:offset+self.size].rstrip("\x00"), self.size)
  180.  
  181.  
  182. class Array(object):
  183.     """Implements arrays for structures. The number of items is written in front
  184.    with a VarInt, followed by every item."""
  185.    
  186.     default = list
  187.        
  188.     def __init__(self, structure):
  189.         self.structure = structure
  190.    
  191.     def serialize(self, inp):
  192.         output = VarInt.serialize(len(inp))
  193.         for x in inp:
  194.             serialized = self.structure.serialize(x)
  195.             output += serialized
  196.         return output
  197.    
  198.     def deserialize(self, bytes, offset = 0):
  199.         (count, countsize) = VarInt.deserialize(bytes, offset)
  200.         totalsize = countsize
  201.         elements = []
  202.         for x in xrange(0, count):
  203.             (element, elementsize) = self.structure.deserialize(bytes,
  204.                 offset + totalsize)
  205.             totalsize += elementsize
  206.             elements.append(element)
  207.         return (elements, totalsize)
  208.    
  209.    
  210. class Structure(object):
  211.     """This class is to be used as a base for structures, which is just a fancy
  212.    way to call a collection of other structures and base types really.
  213.    
  214.    Every structure needs to have a fields attribute: a list of (fieldname,
  215.    fieldtype) tuples."""
  216.    
  217.     fields = []
  218.  
  219.     @classmethod
  220.     def default(kls):
  221.         return kls()
  222.        
  223.     def __init__(self):
  224.         """Constructor."""
  225.         for (fieldname, fieldtype) in self.fields:
  226.             setattr(self, fieldname, fieldtype.default())
  227.    
  228.     def serialize(self):
  229.         """Serializes this structure and returns it as a string."""
  230.         output = ""
  231.        
  232.         for (fieldname, fieldtype) in self.fields:
  233.             value = getattr(self, fieldname)
  234.             encoded = fieldtype.serialize(value)
  235.             output += encoded
  236.        
  237.         return output
  238.    
  239.     @classmethod
  240.     def deserialize(kls, bytes, offset = 0):
  241.         """Deserialize the structure stored in bytes, starting at offset"""
  242.         structure = kls()
  243.         totalsize = 0
  244.  
  245.         for (fieldname, fieldtype) in kls.fields:
  246.             (value, size) = fieldtype.deserialize(bytes, offset)
  247.             setattr(structure, fieldname, value)
  248.             offset += size
  249.             totalsize += size
  250.  
  251.         return (structure, totalsize)
  252.  
  253.     def __repr__(self):
  254.         return self.__class__.__name__ + " (" + \
  255.             ", ".join([fieldname + ": " + repr(getattr(self, fieldname)) \
  256.             for (fieldname, fieldtype) in self.fields]) + \
  257.             ")"
  258.  
  259.  
  260. #Those are the various structures used in the bitcoin protocol
  261. #Taken from: https://en.bitcoin.it/wiki/Protocol_specification
  262. #with some changes to make them more easily manageable.
  263.  
  264. class NetworkAddress(Structure):
  265.     fields = [
  266.         ("services", UInt64),
  267.         ("ipaddress", FixedString(16)),
  268.         ("port", UInt16BigEndian)
  269.     ]
  270.  
  271. class TimestampedNetworkAddress(Structure):
  272.     fields = [
  273.         ("timestamp", UInt32),
  274.         ("services", UInt64),
  275.         ("ipaddress", FixedString(16)),
  276.         ("port", UInt16BigEndian)
  277.     ]
  278.  
  279. class MessageHeader(Structure):
  280.     fields = [
  281.         ("magic", FixedString(4)),
  282.         ("command", FixedString(12)),
  283.         ("length", UInt32)
  284.     ]
  285.  
  286. class Version(Structure):
  287.     fields = [
  288.         ("version", UInt32),
  289.         ("services", UInt64),
  290.         ("timestamp", UInt64),
  291.         ("addrme", NetworkAddress),
  292.         ("addryou", NetworkAddress),
  293.         ("nonce", UInt64),
  294.         ("subversionumber", VarString),
  295.         ("startheight", UInt32)
  296.     ]
  297.  
  298. class Verack(Structure):
  299.     fields = [
  300.     ]
  301.  
  302. class Getblocks(Structure):
  303.     fields = [
  304.         ("version", UInt32),
  305.         ("hashstart", Array(FixedString(32))),
  306.         ("hashstop", FixedString(32))
  307.     ]
  308.  
  309. class Getheaders(Structure):
  310.     fields = [
  311.         ("verion", UInt32),
  312.         ("hashstart", Array(FixedString(32))),
  313.         ("hashstop", FixedString(32))
  314.     ]
  315.    
  316. class BlockHeader(Structure):
  317.     fields = [
  318.         ("version", UInt32),
  319.         ("prevblock", FixedString(32)),
  320.         ("merkleroot", FixedString(32)),
  321.         ("timestamp", UInt32),
  322.         ("difficulty", UInt32),
  323.         ("nonce", UInt32),
  324.         ("txcount", UInt8)
  325.     ]
  326.    
  327.     def calc_hash(self):
  328.         serialized = self.serialize()[:80]
  329.         h = hashlib.sha256(serialized).digest()
  330.         h = hashlib.sha256(h).digest()
  331.         return h
  332.  
  333. class BlockHeaders(Structure):
  334.     fields = [
  335.         ("headers", Array(BlockHeader))
  336.     ]
  337.    
  338. class InvVector(Structure):
  339.     fields = [
  340.         ("type", UInt32),
  341.         ("hash", FixedString(32))
  342.     ]
  343.  
  344. class Inventory(Structure):
  345.     fields = [
  346.         ("inventory", Array(InvVector)),
  347.     ]
  348.  
  349. class GetData(Structure):
  350.     fields = [
  351.         ("inventory", Array(InvVector)),
  352.     ]
  353.  
  354.  
  355. class OutPoint(Structure):
  356.     fields = [
  357.         ("hash", FixedString(32)),
  358.         ("index", UInt32)
  359.     ]
  360.  
  361. class TxIn(Structure):
  362.     fields = [
  363.         ("previousoutput", OutPoint),
  364.         ("script", VarString),
  365.         ("sequence", UInt32)
  366.     ]
  367.  
  368. class TxOut(Structure):
  369.     fields = [
  370.         ("value", UInt64),
  371.         ("script", VarString)
  372.     ]
  373.    
  374. class Tx(Structure):
  375.     fields = [
  376.         ("version", UInt32),
  377.         ("txins", Array(TxIn)),
  378.         ("txout", Array(TxOut)),
  379.         ("locktime", UInt32)
  380.     ]
  381.    
  382.     def calc_hash(self):
  383.         serialized = self.serialize()
  384.         h = hashlib.sha256(serialized).digest()
  385.         h = hashlib.sha256(h).digest()
  386.         return h
  387.    
  388. class Block(Structure):
  389.     fields = [
  390.         ("version", UInt32),
  391.         ("prevblock", FixedString(32)),
  392.         ("merkleroot", FixedString(32)),
  393.         ("timestamp", UInt32),
  394.         ("difficulty", UInt32),
  395.         ("nonce", UInt32),
  396.         ("txns", Array(Tx))
  397.     ]
  398.    
  399.     def calc_hash(self):
  400.         serialized = self.serialize()[:80]
  401.         h = hashlib.sha256(serialized).digest()
  402.         h = hashlib.sha256(h).digest()
  403.         return h
  404.        
  405. class GetAddr(Structure):
  406.     fields = [
  407.     ]
  408.  
  409. class Address(Structure):
  410.     fields = [
  411.         ("addresses", Array(TimestampedNetworkAddress))
  412.     ]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement