PKI|GE҂{bitpeer/__init__.pyfrom . import storage PK2]|GX ))bitpeer/networks.pySUPPORTED_CHAINS = [ "BTC", "XTN", "NMC", "LTC", "XLT", "DOGE", "XDN" ] # Network magic values MAGIC_VALUES = { "BTC": 0xD9B4BEF9, "XTN": 0x0709110B, "NMC": 0xFEB4BEF9, "LTC": 0xFBC0B6DB, "XLT": 0xDCB7C1FC, "DOGE": 0xC0C0C0C0, "XDN": 0xDCB7C1FC } # Genesis blocks GENESIS = { "BTC": 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f, "XTN": 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943, "NMC": 0x000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770, "LTC": 0x12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2, "XLT": 0xf5ae71e26c74beacc88382716aced69cddf3dffff24f384e1808905e0188f68f, "DOGE": 0x1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691, "XDN": 0xbb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e } # Default ports PORTS = { "BTC": 8333, "XTN": 18333, "NMC": 8334, "LTC": 9333, "XLT": 19333, "DOGE": 22556, "XDN": 44556 } # Almost available peers PEERS = { "BTC": [("bitcoin.sipa.be", 8333)], "XTN": [], "NMC": [], "LTC": [], "XLT": [("51.254.215.160", 19333)] } # Seed servers SEEDS = { "BTC": [ "seed.bitcoin.sipa.be", "dnsseed.bluematt.me", "dnsseed.bitcoin.dashjr.org", "seed.bitcoinstats.com", "bitseed.xf2.org"], "XTN": [ "testnet-seed.alexykot.me", "testnet-seed.bitcoin.petertodd.org", "testnet-seed.bluematt.me", "testnet-seed.bitcoin.schildbach.de" ], "LTC": [ "dnsseed.litecointools.com", "dnsseed.litecoinpool.org", "dnsseed.ltc.xurious.com", "dnsseed.koin-project.com", "dnsseed.weminemnc.com" ], "XLT": [ "testnet-seed.litecointools.com", "testnet-seed.ltc.xurious.com", "dnsseed.wemine-testnet.com" ], "NMC": [ "namecoindnsseed.digi-masters.com", "namecoindnsseed.digi-masters.uk", "seed.namecoin.domob.eu", "nmc.seed.quisquis.de", "dnsseed.namecoin.webbtc.com" ], "DOGE": [ "seed.dogecoin.com", "seed.multidoge.org", "seed2.multidoge.org", "seed.doger.dogecoin.com" ], "XDN": [ "testseed.jrn.me.uk" ] } class UnsupportedChainException (Exception): pass def isSupported (chain): return (chain.upper () in SUPPORTED_CHAINS) PK}qGggbitpeer/exceptions.py class NodeDisconnectException(Exception): """This exception is thrown when Protocoin detects a disconnection from the node it is connected.""" pass class InvalidMessageChecksum(Exception): """This exception is thrown when the checksum for a message in a message header doesn't match the actual checksum of the message.""" pass PK~GG!G!bitpeer/node.pyimport binascii import shelve import socket import random from pycoin.tx import Tx #remove this from io import BytesIO from threading import Thread, Lock, Timer from . import clients from . import networks from . import serializers from . import log from . import storage MAX_CLIENT_FAILURES = 5 class NodeClient (clients.ChainClient): def __init__ (self, socket, chain, node, host): self.node = node self.host = host self.failures = 0 super (NodeClient, self).__init__ (socket, chain) def reconnect (self): self.failures += 1 if self.failures < MAX_CLIENT_FAILURES: try: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.settimeout (3.0) self.socket.connect (self.host) self.socket.settimeout (None) self.handshake () except: return False return True else: return False def handle_block(self, message_header, message): self.node.handle_block (message_header, message) def handle_inv(self, message_header, message): getdata = clients.GetData() getdata_serial = clients.GetDataSerializer() getdata.inventory = message.inventory self.send_message(getdata) def handle_mempool (self, message_header, message): self.node.handle_mempool (self, message_header, message) def handle_getdata (self, message_header, message): self.node.handle_getdata (self, message_header, message) class Node: def __init__ (self, chain, storagedb, lastblockhash = None, lastblockheight = None, logger=None, maxpeers=15): if not networks.isSupported (chain): raise networks.UnsupportedChainException () if logger == None: self.logger = log.DummyLogger () else: self.logger = logger if type (storagedb) == str: self.db = storage.shelve.ShelveStorage (storagedb) else: self.db = storagedb self.maxpeers = maxpeers self.minpeers = 4 self.chain = chain self.clients = [] self.threads = [] self.peers = [] self.blockFilter = lambda b: b self.synctimer = None self.mempooltimer = None self.boottimer = None self.postblocks = {} # Create mempool #if not 'mempool' in self.db: self.db['mempool'] = {} # Set current block if ('lastblockheight' in self.db) and (self.db ['lastblockheight'] > lastblockheight): pass elif lastblockheight != None and lastblockhash != None: self.db ['lastblockheight'] = int (lastblockheight) self.db ['lastblockhash'] = lastblockhash else: self.db ['lastblockheight'] = 0 self.db ['lastblockhash'] = networks.GENESIS[chain] self.logger.debug ('Starting sync from block %d %s', self.db['lastblockheight'], self.db['lastblockhash']) # Contact the seed nodes for retrieving a peer list, also load a file peer list def bootstrap (self): self.peers = [] for seed in networks.SEEDS [self.chain]: try: (hostname, aliaslist, ipaddrlist) = socket.gethostbyname_ex (seed) for ip in ipaddrlist: self.peers.append ((ip, networks.PORTS[self.chain])) except Exception as e: pass #print (self.peers) if len (self.peers) == 0: raise Exception () random.shuffle (self.peers) #self.peers = self.peers [0:self.maxpeers] self.logger.debug ('Bootstrap done with %s peers', len (self.peers)) def isConnected (self, peer): for x in self.clients: if x.host == peer: return True return False def connect (self): for peer in self.peers: if self.isConnected (peer): return if len (self.clients) == self.maxpeers: break try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout (3.0) sock.connect (peer) sock.settimeout (None) pcc = NodeClient (sock, self.chain, self, peer) pcc.handshake () self.clients.append (pcc) except: pass if len (self.clients) == 0: raise Exception () self.logger.debug ('Connected to %s peers', len (self.clients)) self.synctimer = Timer (5.0, self.sync) self.synctimer.start () def reboot (self): self.logger.debug ('Bootstrapping clients...') self.boottimer.cancel () self.bootstrap () self.connect () def sync (self): if len (self.clients) == 0: self.logger.debug ('No clients available, bootstrapping...') self.reboot () return if len (self.clients) < self.minpeers: self.logger.debug ('Available clients are less than minpeers, bootstrapping...') if self.boottimer != None: self.boottimer.cancel () self.boottimer = Timer (5.0, self.reboot) self.boottimer.start () for p in self.clients: #r = random.randint(0, len (self.clients) - 1) #p = self.clients [r] try: getblock = clients.GetBlocks ([int (self.db['lastblockhash'], 16)]) p.send_message (getblock) except Exception as e: #self.logger.error ('Node fail.') if not p.reconnect (): self.logger.debug ('Removed unreachable peer %s (%s)', str (p.host), e) self.clients.remove (p) self.logger.debug ('Available peers: %d', len (self.clients)) self.synctimer.cancel () self.synctimer = Timer (5.0, self.sync) self.synctimer.start () def innerLoop (self, cl): try: cl.loop () except Exception as e: #self.logger.error ('Node loop failure.') if not cl.reconnect (): self.logger.debug ('Removed unreachable peer %s (%s)', str (cl.host), e) self.clients.remove (cl) self.logger.debug ('Available peers: %d', len (self.clients)) def loop (self): self.mempooltimer = Timer (1.0, self.announceTransactions) self.mempooltimer.start () # Start the loop in each client as a new thread for cl in self.clients: t = Thread (target=self.innerLoop, args=(cl,)) t.start () self.threads.append (t) def stop (self): for cl in self.clients: cl.stop () for t in self.threads: t.join () def announceTransactions (self, txid = None): if txid != None or len (self.db['mempool']) != 0: inv = clients.InventoryVector() inv_serial = clients.InventoryVectorSerializer() if txid != None: sinv = clients.Inventory () sinv.inv_hash = int (txid, 16) inv.inventory = [ sinv ] else: inv.inventory = [] for txid in self.db['mempool']: sinv = clients.Inventory () sinv.inv_hash = int (txid, 16) inv.inventory.append (sinv) #self.logger.debug ('Announcing %d transactions', len (inv.inventory)) for cl in self.clients: try: cl.send_message (inv) except Exception as e: pass #logger.debug ('Transaction announce failure: %s', str (e)) self.mempooltimer.cancel () self.mempooltimer = Timer (30.0, self.announceTransactions) self.mempooltimer.start () def handle_mempool (self, client, message_header, message): pass def handle_getdata (self, client, message_header, message): for inv in message.inventory: txhash = str (hex (inv.inv_hash))[2:] if txhash in self.db['mempool']: tx = self.db['mempool'][txhash] txhex = tx.as_hex () try: client.send_tx (txhex) except Exception as e: pass #logger.debug ('Send transaction failure: %s', str (e)) def handle_block (self, message_header, message): if message.previous_block_id () == self.db['lastblockhash']: try: b = self.blockFilter (message) except Exception as e: logger.debug ('BlockFilter failure: %s', str (e)) # Serialize block hash = message.id () self.db[str (int (self.db['lastblockheight']) + 1)] = hash self.db[hash] = b self.db['lastblockheight'] += 1 self.db['lastblockhash'] = hash self.logger.debug ('New block: %d %s', self.db['lastblockheight'], self.db['lastblockhash']) if hash in self.postblocks: self.handle_block (None, self.prevblocks[hash]) #self.logger.debug ('PREVBLOCK found') del self.postblocks[hash] self.db.sync () self.synctimer.cancel () self.synctimer = Timer (0.5, self.sync) self.synctimer.start () else: hash = message.previous_block_id () if not hash in self.db: self.postblocks [hash] = message def getLastBlockHeight (self): return self.db['lastblockheight'] def getBlockHash (self, index): if str(index) in self.db: return self.db[str(index)] else: return None def getBlockByHash (self, bhash): if bhash in self.db: return self.db[bhash] else: return None def broadcastTransaction (self, transaction): t = Tx.tx_from_hex (transaction) h = t.id () if not h in self.db['mempool']: mp = self.db['mempool'] mp[h] = t self.db['mempool'] = mp self.db.sync () self.mempooltimer = Timer (1.0, self.announceTransactions) self.mempooltimer.start () return h PKTZ|G{gbitpeer/clients.pyimport binascii from io import BytesIO from .serializers import * from pycoin.block import Block from .exceptions import NodeDisconnectException, InvalidMessageChecksum import os import codecs class ProtocolBuffer (object): def __init__ (self): self.buffer = BytesIO() self.header_size = MessageHeaderSerializer.calcsize() #self.socket = socket def write (self, data): self.buffer.write(data) def receive_message (self): """This method will attempt to extract a header and message. It will return a tuple of (header, message) and set whichever can be set so far (None otherwise). """ # Calculate the size of the buffer self.buffer.seek(0, os.SEEK_END) buffer_size = self.buffer.tell() # Check if a complete header is present if buffer_size < self.header_size: return (None, None) # Go to the beginning of the buffer self.buffer.seek(0) message_model = None message_header_serial = MessageHeaderSerializer() message_header = message_header_serial.deserialize(self.buffer) total_length = self.header_size + message_header.length # Incomplete message if buffer_size < total_length: self.buffer.seek(0, os.SEEK_END) return (message_header, None) payload = self.buffer.read(message_header.length) #print (codecs.encode (payload, 'hex')) remaining = self.buffer.read() self.buffer = BytesIO() self.buffer.write(remaining) payload_checksum = MessageHeaderSerializer.calc_checksum(payload) # Check if the checksum is valid if payload_checksum != message_header.checksum: msg = "Bad checksum for command %s" % message_header.command raise InvalidMessageChecksum(msg) if message_header.command in MESSAGE_MAPPING: #print (message_header.command) if message_header.command == 'block': message_model = Block.parse(BytesIO(payload)) #print (message_model.id ()) else: deserializer = MESSAGE_MAPPING[message_header.command]() message_model = deserializer.deserialize(BytesIO(payload)) return (message_header, message_model) class ChainBasicClient (object): """The base class for a Bitcoin network client, this class implements utility functions to create your own class. :param socket: a socket that supports the makefile() method. """ def __init__(self, socket, chain = 'BTC'): if not networks.isSupported (chain): raise networks.UnsupportedChainException () self.chain = chain.upper () self.socket = socket self.buffer = ProtocolBuffer() self.run = True def stop (self): self.run = False time.sleep (5) if self.socket != None: self.socket.close () def update_socket (self, socket): self.socket = socket def close_stream(self): """This method will close the socket stream.""" self.socket.close() def handle_message_header(self, message_header, payload): """This method will be called for every message before the message payload deserialization. :param message_header: The message header :param payload: The payload of the message """ pass def handle_send_message(self, message_header, message): """This method will be called for every sent message. :param message_header: The header of the message :param message: The message to be sent """ pass def send_tx (self, message): bin_data = BytesIO() message_header = MessageHeader(self.chain) message_header_serial = MessageHeaderSerializer() bin_message = binascii.unhexlify (message) payload_checksum = MessageHeaderSerializer.calc_checksum(bin_message) message_header.checksum = payload_checksum message_header.length = len(bin_message) message_header.command = 'tx' bin_data.write(message_header_serial.serialize(message_header)) bin_data.write(bin_message) self.socket.sendall(bin_data.getvalue()) self.handle_send_message(message_header, message) def send_message(self, message): """This method will serialize the message using the appropriate serializer based on the message command and then it will send it to the socket stream. :param message: The message object to send """ bin_data = BytesIO() message_header = MessageHeader(self.chain) message_header_serial = MessageHeaderSerializer() serializer = MESSAGE_MAPPING[message.command]() bin_message = serializer.serialize(message) payload_checksum = \ MessageHeaderSerializer.calc_checksum(bin_message) message_header.checksum = payload_checksum message_header.length = len(bin_message) message_header.command = message.command bin_data.write(message_header_serial.serialize(message_header)) bin_data.write(bin_message) self.socket.sendall(bin_data.getvalue()) self.handle_send_message(message_header, message) def loop(self): """This is the main method of the client, it will enter in a receive/send loop.""" while self.run: data = self.socket.recv(1024*8) if len(data) <= 0: raise NodeDisconnectException("Node disconnected") self.buffer.write(data) message_header, message = self.buffer.receive_message() if message_header is not None: self.handle_message_header(message_header, data) if not message: continue handle_func_name = "handle_" + message_header.command handle_func = getattr(self, handle_func_name, None) if handle_func: handle_func(message_header, message) class ChainClient(ChainBasicClient): """This class implements all the protocol rules needed for a client to stay up in the network. It will handle the handshake rules as well answer the ping messages.""" def handshake(self): """This method will implement the handshake of the Bitcoin protocol. It will send the Version message.""" version = Version() self.send_message(version) def handle_version(self, message_header, message): """This method will handle the Version message and will send a VerAck message when it receives the Version message. :param message_header: The Version message header :param message: The Version message """ verack = VerAck() self.send_message(verack) def handle_ping (self, message_header, message): """This method will handle the Ping message and then will answer every Ping message with a Pong message using the nonce received. :param message_header: The header of the Ping message :param message: The Ping message """ pong = Pong() pong.nonce = message.nonce self.send_message(pong) PK2{G\**DDbitpeer/serializers.pyimport binascii import time import random import hashlib import struct from io import BytesIO from collections import OrderedDict from . import fields, networks class SerializerMeta(type): """The serializer meta class. This class will create an attribute called '_fields' in each serializer with the ordered dict of fields present on the subclasses. """ def __new__(meta, name, bases, attrs): attrs["_fields"] = meta.get_fields(bases, attrs, fields.Field) return super(SerializerMeta, meta).__new__(meta, name, bases, attrs) @classmethod def get_fields(meta, bases, attrs, field_class): """This method will construct an ordered dict with all the fields present on the serializer classes.""" fields = [(field_name, attrs.pop(field_name)) for field_name, field_value in list(attrs.items()) if isinstance(field_value, field_class)] for base_cls in bases[::-1]: if hasattr(base_cls, "_fields"): fields = list(base_cls._fields.items()) + fields fields.sort(key=lambda it: it[1].count) return OrderedDict(fields) class SerializerABC(object, metaclass = SerializerMeta): """The serializer abstract base class.""" class Serializer(SerializerABC): """The main serializer class, inherit from this class to create custom serializers. Example of use:: class VerAckSerializer(Serializer): model_class = VerAck """ def serialize(self, obj, fields=None): """This method will receive an object and then will serialize it according to the fields declared on the serializer. :param obj: The object to serializer. """ bin_data = BytesIO() for field_name, field_obj in self._fields.items(): if fields: if field_name not in fields: continue attr = getattr(obj, field_name, None) field_obj.parse(attr) bin_data.write(field_obj.serialize()) return bin_data.getvalue() def deserialize(self, stream): """This method will read the stream and then will deserialize the binary data information present on it. :param stream: A file-like object (BytesIO, file, socket, etc.) """ model = self.model_class() for field_name, field_obj in self._fields.items(): value = field_obj.deserialize(stream) setattr(model, field_name, value) return model class MessageHeader(object): """The header of all bitcoin messages.""" def __init__(self, chain="BTC"): self.magic = networks.MAGIC_VALUES[chain] self.command = "None" self.length = 0 self.checksum = 0 def _magic_to_text(self): """Converts the magic value to a textual representation.""" for k, v in networks.MAGIC_VALUES.items(): if v == self.magic: return k return "Unknown Magic" def __repr__(self): return "<%s Magic=[%s] Length=[%d] Checksum=[%d]>" % \ (self.__class__.__name__, self._magic_to_text(), self.length, self.checksum) class MessageHeaderSerializer(Serializer): """Serializer for the MessageHeader.""" model_class = MessageHeader magic = fields.UInt32LEField() command = fields.FixedStringField(12) length = fields.UInt32LEField() checksum = fields.UInt32LEField() @staticmethod def calcsize(): return struct.calcsize("i12sii") @staticmethod def calc_checksum(payload): """Calculate the checksum of the specified payload. :param payload: The binary data payload. """ sha256hash = hashlib.sha256(payload) sha256hash = hashlib.sha256(sha256hash.digest()) checksum = sha256hash.digest()[:4] return struct.unpack("" % (self.__class__.__name__, self.ip_address, self.port, services) class IPv4AddressSerializer(Serializer): """Serializer for the IPv4Address.""" model_class = IPv4Address services = fields.UInt64LEField() ip_address = fields.IPv4AddressField() port = fields.UInt16BEField() class IPv4AddressTimestamp(IPv4Address): """The IPv4 Address with timestamp.""" def __init__(self): super(IPv4AddressTimestamp, self).__init__() self.timestamp = int(time.time()) def __repr__(self): services = self._services_to_text() if not services: services = "No Services" return "<%s Timestamp=[%s] IP=[%s:%d] Services=%r>" % \ (self.__class__.__name__, time.ctime(self.timestamp), self.ip_address, self.port, services) class IPv4AddressTimestampSerializer(Serializer): """Serializer for the IPv4AddressTimestamp.""" model_class = IPv4AddressTimestamp timestamp = fields.UInt32LEField() services = fields.UInt64LEField() ip_address = fields.IPv4AddressField() port = fields.UInt16BEField() class Version(object): """The version command.""" command = "version" def __init__(self): self.version = fields.PROTOCOL_VERSION self.services = fields.SERVICES["NODE_NETWORK"] self.timestamp = int(time.time()) self.addr_recv = IPv4Address() self.addr_from = IPv4Address() self.nonce = random.randint(0, 2**32-1) self.user_agent = "/bitpeer.py:0.10.2/" self.start_height = 0 class VersionSerializer(Serializer): """The version command serializer.""" model_class = Version version = fields.Int32LEField() services = fields.UInt64LEField() timestamp = fields.Int64LEField() addr_recv = fields.NestedField(IPv4AddressSerializer) addr_from = fields.NestedField(IPv4AddressSerializer) nonce = fields.UInt64LEField() user_agent = fields.VariableStringField() start_height = fields.Int32LEField() class VerAck(object): """The version acknowledge (verack) command.""" command = "verack" class VerAckSerializer(Serializer): """The serializer for the verack command.""" model_class = VerAck class Ping(object): """The ping command, which should always be answered with a Pong.""" command = "ping" def __init__(self): self.nonce = random.randint(0, 2**32-1) def __repr__(self): return "<%s Nonce=[%d]>" % (self.__class__.__name__, self.nonce) class PingSerializer(Serializer): """The ping command serializer.""" model_class = Ping nonce = fields.UInt64LEField() class Pong(object): """The pong command, usually returned when a ping command arrives.""" command = "pong" def __init__(self): self.nonce = random.randint(0, 2**32-1) def __repr__(self): return "<%s Nonce=[%d]>" % (self.__class__.__name__, self.nonce) class PongSerializer(Serializer): """The pong command serializer.""" model_class = Pong nonce = fields.UInt64LEField() class Inventory(object): """The Inventory representation.""" def __init__(self): self.inv_type = fields.INVENTORY_TYPE["MSG_TX"] self.inv_hash = 0 def type_to_text(self): """Converts the inventory type to text representation.""" for k, v in fields.INVENTORY_TYPE.items(): if v == self.inv_type: return k return "Unknown Type" def __repr__(self): return "<%s Type=[%s] Hash=[%064x]>" % \ (self.__class__.__name__, self.type_to_text(), self.inv_hash) class InventorySerializer(Serializer): """The serializer for the Inventory.""" model_class = Inventory inv_type = fields.UInt32LEField() inv_hash = fields.Hash() class InventoryVector(object): """A vector of inventories.""" command = "inv" def __init__(self): self.inventory = [] def __repr__(self): return "<%s Count=[%d]>" % (self.__class__.__name__, len(self)) def __len__(self): return len(self.inventory) def __iter__(self): return iter(self.inventory) class InventoryVectorSerializer(Serializer): """The serializer for the vector of inventories.""" model_class = InventoryVector inventory = fields.ListField(InventorySerializer) class AddressVector(object): """A vector of addresses.""" command = "addr" def __init__(self): self.addresses = [] def __repr__(self): return "<%s Count=[%d]>" % (self.__class__.__name__, len(self)) def __len__(self): return len(self.addresses) def __iter__(self): return iter(self.addresses) class AddressVectorSerializer(Serializer): """Serializer for the addresses vector.""" model_class = AddressVector addresses = fields.ListField(IPv4AddressTimestampSerializer) class GetData(InventoryVector): """GetData message command.""" command = "getdata" class GetDataSerializer(Serializer): """Serializer for the GetData command.""" model_class = GetData inventory = fields.ListField(InventorySerializer) class NotFound(GetData): """NotFound command message.""" command = "notfound" class NotFoundSerializer(Serializer): """Serializer for the NotFound message.""" model_class = NotFound inventory = fields.ListField(InventorySerializer) class OutPoint(object): """The OutPoint representation.""" def __init__(self): self.out_hash = 0 self.index = 0 def __repr__(self): return "<%s Index=[%d] Hash=[%064x]>" % \ (self.__class__.__name__, self.index, self.out_hash) class OutPointSerializer(Serializer): """The OutPoint representation serializer.""" model_class = OutPoint out_hash = fields.Hash() index = fields.UInt32LEField() class TxIn(object): """The transaction input representation.""" def __init__(self): self.previous_output = None self.signature_script = "Empty" self.sequence = 4294967295 def __repr__(self): return "<%s Sequence=[%d]>" % \ (self.__class__.__name__, self.sequence) class TxInSerializer(Serializer): """The transaction input serializer.""" model_class = TxIn previous_output = fields.NestedField(OutPointSerializer) signature_script = fields.VariableStringField() sequence = fields.UInt32LEField() class TxOut(object): """The transaction output.""" def __init__(self): self.value = 0 self.pk_script = "Empty" def get_btc_value(self): return self.value//100000000 + self.value%100000000/100000000.0 def __repr__(self): return "<%s Value=[%.8f]>" % (self.__class__.__name__, self.get_btc_value()) class TxOutSerializer(Serializer): """The transaction output serializer.""" model_class = TxOut value = fields.Int64LEField() pk_script = fields.VariableStringField() class Tx(object): """The main transaction representation, this object will contain all the inputs and outputs of the transaction.""" command = "tx" def __init__(self): self.version = 1 self.tx_in = [] self.tx_out = [] self.lock_time = 0 def _locktime_to_text(self): """Converts the lock-time to textual representation.""" text = "Unknown" if self.lock_time == 0: text = "Always Locked" elif self.lock_time < 500000000: text = "Block %d" % self.lock_time elif self.lock_time >= 500000000: text = time.ctime(self.lock_time) return text def calculate_hash(self): """This method will calculate the hash of the transaction.""" hash_fields = ["version", "tx_in", "tx_out", "lock_time"] serializer = TxSerializer() bin_data = serializer.serialize(self, hash_fields) h = hashlib.sha256(bin_data).digest() h = hashlib.sha256(h).digest() return binascii.b2a_hex (h[::-1]) def __repr__(self): return "<%s Version=[%d] Lock Time=[%s] TxIn Count=[%d] Hash=[%s] TxOut Count=[%d]>" \ % (self.__class__.__name__, self.version, self._locktime_to_text(), len(self.tx_in), self.calculate_hash(), len(self.tx_out)) class TxSerializer(Serializer): """The transaction serializer.""" model_class = Tx version = fields.UInt32LEField () tx_in = fields.ListField (TxInSerializer) tx_out = fields.ListField (TxOutSerializer) lock_time = fields.UInt32LEField () class BlockHeader(object): """The header of the block.""" def __init__(self): self.version = 0 self.prev_block = 0 self.merkle_root = 0 self.timestamp = 0 self.bits = 0 self.nonce = 0 self.txns_count = 0 def calculate_hash(self): """This method will calculate the hash of the block.""" hash_fields = ["version", "prev_block", "merkle_root", "timestamp", "bits", "nonce"] serializer = BlockSerializer() bin_data = serializer.serialize(self, hash_fields) h = hashlib.sha256(bin_data).digest() h = hashlib.sha256(h).digest() return binascii.b2a_hex (h[::-1]) def __repr__(self): return "<%s Version=[%d] Timestamp=[%s] Nonce=[%d] Hash=[%s] Tx Count=[%d]>" % \ (self.__class__.__name__, self.version, time.ctime(self.timestamp), self.nonce, self.calculate_hash(), self.txns_count) class BlockHeaderSerializer(Serializer): """The serializer for the block header.""" model_class = BlockHeader version = fields.UInt32LEField() prev_block = fields.Hash() merkle_root = fields.Hash() timestamp = fields.UInt32LEField() bits = fields.UInt32LEField() nonce = fields.UInt32LEField() txns_count = fields.VariableIntegerField() class Block(BlockHeader): """The block message. This message contains all the transactions present in the block.""" command = "block" def __init__(self): self.version = 0 self.prev_block = 0 self.merkle_root = 0 self.timestamp = 0 self.bits = 0 self.nonce = 0 self.txns = [] def __len__(self): return len(self.txns) def __iter__(self): return iter(self.txns) def __repr__(self): return "<%s Version=[%d] Timestamp=[%s] Nonce=[%d] Hash=[%s] Tx Count=[%d]>" % \ (self.__class__.__name__, self.version, time.ctime(self.timestamp), self.nonce, self.calculate_hash(), len(self)) class BlockSerializer(Serializer): """The deserializer for the blocks.""" model_class = Block version = fields.UInt32LEField() prev_block = fields.Hash() merkle_root = fields.Hash() timestamp = fields.UInt32LEField() bits = fields.UInt32LEField() nonce = fields.UInt32LEField() txns = fields.ListField(TxSerializer) class HeaderVector(object): """The header only vector.""" command = "headers" def __init__(self): self.headers = [] def __repr__(self): return "<%s Count=[%d]>" % (self.__class__.__name__, len(self)) def __len__(self): return len(self.headers) def __iter__(self): return iter(self.headers) class HeaderVectorSerializer(Serializer): """Serializer for the block header vector.""" model_class = HeaderVector headers = fields.ListField(BlockHeaderSerializer) class MemPool(object): """The mempool command.""" command = "mempool" class MemPoolSerializer(Serializer): """The serializer for the mempool command.""" model_class = MemPool class GetAddr(object): """The getaddr command.""" command = "getaddr" class GetAddrSerializer(Serializer): """The serializer for the getaddr command.""" model_class = GetAddr class GetBlocks(object): """The getblocks command.""" command = "getblocks" def __init__(self, hashes): self.version = fields.PROTOCOL_VERSION self.hash_count = len(hashes) self.hash_stop = 0 self.block_hashes = hashes class GetBlocksSerializer(Serializer): model_class = GetBlocks version = fields.UInt32LEField() hash_count = fields.VariableIntegerField() block_hashes = fields.BlockLocator() hash_stop = fields.Hash() MESSAGE_MAPPING = { "version": VersionSerializer, "verack": VerAckSerializer, "ping": PingSerializer, "pong": PongSerializer, "inv": InventoryVectorSerializer, "addr": AddressVectorSerializer, "getdata": GetDataSerializer, "notfound": NotFoundSerializer, "tx": TxSerializer, "block": BlockSerializer, "headers": HeaderVectorSerializer, "mempool": MemPoolSerializer, "getaddr": GetAddrSerializer, "getblocks": GetBlocksSerializer, } PK}qG;3bitpeer/util.py# The Base58 digits base58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' def base58_encode(address_bignum): """This function converts an address in bignum formatting to a string in base58, it doesn't prepend the '1' prefix for the Bitcoin address. :param address_bignum: The address in numeric format :returns: The string in base58 """ basedigits = [] while address_bignum > 0: address_bignum, rem = divmod(address_bignum, 58) basedigits.insert(0, base58_digits[rem]) return ''.join(basedigits) def base58_decode(address): """This function converts an base58 string to a numeric format. :param address: The base58 string :returns: The numeric value decoded """ address_bignum = 0 for char in address: address_bignum *= 58 digit = base58_digits.index(char) address_bignum += digit return address_bignumPK{G!q*q*bitpeer/fields.py from .exceptions import NodeDisconnectException from io import BytesIO import struct import time import random import socket #: The protocol version PROTOCOL_VERSION = 70002 #: The available services SERVICES = { "NODE_NETWORK": 0x1, } #: The type of the inventories INVENTORY_TYPE = { "ERROR": 0, "MSG_TX": 1, "MSG_BLOCK": 2, } 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 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 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 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) 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 = ">= 32 return bin_data.getvalue() class BlockLocator(Field): """A block locator type used for getblocks and getheaders""" datatype = ">= 32 return bin_data.getvalue() PK}qGȊbitpeer/keys.pyimport binascii import hashlib import ecdsa from . import util class BitcoinPublicKey(object): """This is a representation for Bitcoin public keys. In this class you'll find methods to import/export keys from multiple formats. Use a hex string representation to construct a new public key or use the clas methods to import from another format. :param hexkey: The key in hex string format """ key_prefix = '\x04' def __init__(self, hexkey): stringkey = hexkey.decode("hex")[1:] self.public_key = ecdsa.VerifyingKey.from_string(stringkey, curve=ecdsa.SECP256k1) @classmethod def from_private_key(klass, private_key): """This class method will create a new Public Key based on a private key. :param private_key: The private key :returns: a new public key """ public_key = private_key.get_verifying_key() hexkey = public_key.to_string().encode("hex") return klass(hexkey) def to_string(self): """This method will convert the public key to a string representation. :returns: String representation of the public key """ return self.key_prefix + self.public_key.to_string() def to_hex(self): """This method will convert the public key to a hex string representation. :returns: Hex string representation of the public key """ hexkey = self.public_key.to_string().encode("hex") return self.key_prefix.encode("hex") + hexkey.upper() def to_address(self): """This method will convert the public key to a bitcoin address. :returns: bitcoin address for the public key """ pubkeystr = self.to_string() sha256digest = hashlib.sha256(pubkeystr).digest() ripemd160 = hashlib.new('ripemd160') ripemd160.update(sha256digest) ripemd160_digest = ripemd160.digest() # Prepend the version info ripemd160_digest = '\x00' + ripemd160_digest # Calc checksum checksum = hashlib.sha256(ripemd160_digest).digest() checksum = hashlib.sha256(checksum).digest() checksum = checksum[:4] # Append checksum address = ripemd160_digest + checksum address_bignum = int('0x' + address.encode('hex'), 16) base58 = util.base58_encode(address_bignum) return '1' + base58 def __repr__(self): return "" % self.to_address() class BitcoinPrivateKey(object): """This is a representation for Bitcoin private keys. In this class you'll find methods to import/export keys from multiple formats. Use a hex string representation to construct a new Public Key or use the clas methods to import from another format. If no parameter is specified on the construction of this class, a new Private Key will be created. :param hexkey: The key in hex string format :param entropy: A function that accepts a parameter with the number of bytes and returns the same amount of bytes of random data, use a good source of entropy. When this parameter is ommited, the OS entropy source is used. """ wif_prefix = '\x80' def __init__(self, hexkey=None, entropy=None): if hexkey: stringkey = hexkey.decode("hex") self.private_key = \ ecdsa.SigningKey.from_string(stringkey, curve=ecdsa.SECP256k1) else: self.private_key = \ ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1, entropy=entropy) @classmethod def from_string(klass, stringkey): """This method will create a new Private Key using the specified string data. :param stringkey: The key in string format :returns: A new Private Key """ hexvalue = stringkey.encode("hex") return klass(hexvalue) @classmethod def from_wif(klass, wifkey): """This method will create a new Private Key from a WIF format string. :param wifkey: The private key in WIF format :returns: A new Private Key """ value = util.base58_decode(wifkey) hexkey = "%x" % value checksum = hexkey[-4*2:].decode("hex") key = hexkey[:-4*2].decode("hex") shafirst = hashlib.sha256(key).digest() shasecond = hashlib.sha256(shafirst).digest() if shasecond[:4]!=checksum: raise RuntimeError("Invalid checksum for the address.") return klass(key[1:].encode("hex")) def to_hex(self): """This method will convert the Private Key to a hex string representation. :returns: Hex string representation of the Private Key """ hexkey = self.private_key.to_string().encode("hex") return hexkey.upper() def to_string(self): """This method will convert the Private Key to a string representation. :returns: String representation of the Private Key """ return self.private_key.to_string() def to_wif(self): """This method will export the Private Key to WIF (Wallet Import Format). :returns:: The Private Key in WIF format. """ extendedkey = self.wif_prefix + self.to_string() shafirst = hashlib.sha256(extendedkey).digest() shasecond = hashlib.sha256(shafirst).digest() checksum = shasecond[:4] extendedkey = extendedkey + checksum key_bignum = int('0x' + extendedkey.encode('hex'), 16) base58 = util.base58_encode(key_bignum) return base58 def generate_public_key(self): """This method will create a new Public Key based on this Private Key. :returns: A new Public Key """ hexkey = self.to_hex().upper() return BitcoinPublicKey.from_private_key(self.private_key) def __repr__(self): return "" % self.to_hex() PK{G<--bitpeer/log.pyclass DummyLogger: def debug (self, *arg): pass def info (self, *arg): pass def error (self, *arg): pass def critical (self, *arg): pass PKI|G[%bitpeer/storage/__init__.pyfrom . import shelve PKV|G%%bitpeer/storage/mem.pyfrom . import storage class MemStorage (storage.Storage): def __init__ (self): self.db = {} def __getitem__(self, key): return self.db[key] def __setitem__(self, key, value): self.db[key] = value def __contains__(self, key): return key in self.db def sync (self): pass PK\U|GU,bitpeer/storage/storage.pyclass Storage: def __init__ (self, f): raise Exception ('This is an interface') def __getitem__(self, key): raise Exception ('This is an interface') def __setitem__(self, key, value): raise Exception ('This is an interface') def __contains__(self, key): raise Exception ('This is an interface') def sync (self): raise Exception ('This is an interface') PKfU|GɵBrrbitpeer/storage/shelve.pyfrom . import storage import shelve class ShelveStorage (storage.Storage): def __init__ (self, f): self.shelve = shelve.open (f) def __getitem__(self, key): return self.shelve[key] def __setitem__(self, key, value): self.shelve[key] = value def __contains__(self, key): return key in self.shelve def sync (self): return self.shelve.sync () PK~G]c22*bitpeer.py-0.4.7.dist-info/DESCRIPTION.rstA pure Python3 bitcoin protocol implementation. PK~GI&T``(bitpeer.py-0.4.7.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "gessadavide@gmail.com, christian.perone@gmail.com", "name": "Davide Gessa, Christian S. Perone", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/dakk/bitpeer.py"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["bitcoin", "protocol", "blockchain", "litecoin", "testnet", "bitpeer", "peer"], "license": "BSD License", "metadata_version": "2.0", "name": "bitpeer.py", "platform": "Any", "run_requires": [{"requires": ["ecdsa (>=0.10)", "pycoin"]}], "summary": "A pure Python3 bitcoin protocol implementation.", "version": "0.4.7"}PK~G"?(bitpeer.py-0.4.7.dist-info/top_level.txtbitpeer PK'K|G2#bitpeer.py-0.4.7.dist-info/zip-safe PK~G}\\ bitpeer.py-0.4.7.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PK~GM #bitpeer.py-0.4.7.dist-info/METADATAMetadata-Version: 2.0 Name: bitpeer.py Version: 0.4.7 Summary: A pure Python3 bitcoin protocol implementation. Home-page: https://github.com/dakk/bitpeer.py Author: Davide Gessa, Christian S. Perone Author-email: gessadavide@gmail.com, christian.perone@gmail.com License: BSD License Keywords: bitcoin,protocol,blockchain,litecoin,testnet,bitpeer,peer Platform: Any Classifier: Development Status :: 4 - Beta Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Dist: ecdsa (>=0.10) Requires-Dist: pycoin A pure Python3 bitcoin protocol implementation. PK~GBK!bitpeer.py-0.4.7.dist-info/RECORDbitpeer/__init__.py,sha256=6k13463nBGQeJVi1WpWiDcI-53vptBfcPrlvVbzzXbU,23 bitpeer/clients.py,sha256=uPVwxMo_V7BPx8SxukbJiyMBdxBbjmlhBMxej_grg28,6361 bitpeer/exceptions.py,sha256=wzM-xDMhE_ZinpbbhpBiCzISaBSg7_75Gi3ADy-mFIA,359 bitpeer/fields.py,sha256=0it6nSdB0S0lCwKxsqjcYO4cht3xXnRq0ZyIPQalplk,10865 bitpeer/keys.py,sha256=GG8O1K2cf4AKUYifihViduWRwt7msBE7Udef9Hy-gDk,6175 bitpeer/log.py,sha256=l3tKoRJG7L9l3zMUO7RHXdmTYVeDwttNmMSZH2ue1ak,149 bitpeer/networks.py,sha256=2LIgOzN_tseMMLZI3lQTYx1dK2g5Xg_dagZjzQIEJ6o,2089 bitpeer/node.py,sha256=_GDl-5bURE6N9Cyy5cHODpoerKmDF4GfyMB0YWQ1bT4,8519 bitpeer/serializers.py,sha256=lanTTfSd3sfbpM5dDCzd7UHe7rvyFJexCCu5VbcJiN8,17608 bitpeer/util.py,sha256=IGegjWp-vpa9r9_iSmy-f03wIvF3KZov-gbRtdz_vSM,937 bitpeer/storage/__init__.py,sha256=EmndedPtlEKASIse7EmgyRFyEsIX3jk9NUvmk9W5pLw,21 bitpeer/storage/mem.py,sha256=zQAWRgeYQkVSpR_j_6V7FGL2nWxp17MPdbQYwpbKzHk,293 bitpeer/storage/shelve.py,sha256=pi3tJOIYHXnexPQ37c2tcxETN2of9WGIizrbWuPCUv4,370 bitpeer/storage/storage.py,sha256=CP2DRtPROGO849YIjjKUsjxuyH08GfhbG15q5t0TaQA,385 bitpeer.py-0.4.7.dist-info/DESCRIPTION.rst,sha256=SsWixXXlxwgK0v1OUGYUgm15JBkTKTcJImI_JwalHfc,50 bitpeer.py-0.4.7.dist-info/METADATA,sha256=Oyxtl_59NdGyvXjJ5V0wvrlRWAKAGnJH5efVd2Ju6Ec,675 bitpeer.py-0.4.7.dist-info/RECORD,, bitpeer.py-0.4.7.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 bitpeer.py-0.4.7.dist-info/metadata.json,sha256=qE8dS05V2_ynwTK0343ay0ipj-dvWNPHxtSWIhex81M,864 bitpeer.py-0.4.7.dist-info/top_level.txt,sha256=0kAW7jIoS5DggvKpjRbrdyjPSSNF9DdnTOJVv8mFbOM,8 bitpeer.py-0.4.7.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 PKI|GE҂{bitpeer/__init__.pyPK2]|GX ))Hbitpeer/networks.pyPK}qGggbitpeer/exceptions.pyPK~GG!G!< bitpeer/node.pyPKTZ|G{g+bitpeer/clients.pyPK2{G\**DDDbitpeer/serializers.pyPK}qG;3bitpeer/util.pyPK{G!q*q*bitpeer/fields.pyPK}qGȊ+bitpeer/keys.pyPK{G<--wbitpeer/log.pyPKI|G[%8bitpeer/storage/__init__.pyPKV|G%%bitpeer/storage/mem.pyPK\U|GU,bitpeer/storage/storage.pyPKfU|GɵBrrbitpeer/storage/shelve.pyPK~G]c22*Abitpeer.py-0.4.7.dist-info/DESCRIPTION.rstPK~GI&T``(bitpeer.py-0.4.7.dist-info/metadata.jsonPK~G"?(abitpeer.py-0.4.7.dist-info/top_level.txtPK'K|G2#bitpeer.py-0.4.7.dist-info/zip-safePK~G}\\ bitpeer.py-0.4.7.dist-info/WHEELPK~GM #bitpeer.py-0.4.7.dist-info/METADATAPK~GBK!obitpeer.py-0.4.7.dist-info/RECORDPK: