#!/usr/bin/python # # node.py - Bitcoin P2P network half-a-node # # Distributed under the MIT/X11 software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # import struct import socket import asyncore import binascii import time import sys import re import random import cStringIO import hashlib MY_VERSION = 312 MY_SUBVERSION = "/pynode:0.0.1/" # Default Settings if no configuration file is given settings = { "host": "127.0.0.1", "port": 8333, "debug": False, "network": "mainnet" } def new_block_event(block): if block.is_valid(): print " - Valid Block: %s" % block.hash else: print " - Invalid Block: %s" % block.hash def new_transaction_event(tx): if tx.is_valid(): print " - Valid TX: %s" % tx.hash else: print " - Invalid TX: %s" % tx.hash def sha256(s): return hashlib.new('sha256', s).digest() def hash256(s): return sha256(sha256(s)) def deser_string(f): nit = struct.unpack(">= 32 return rs def uint256_from_str(s): r = 0L t = struct.unpack("> 24) & 0xFF v = (c & 0xFFFFFFL) << (8 * (nbytes - 3)) return v def deser_vector(f, c): nit = struct.unpack("H", f.read(2))[0] def serialize(self): r = "" r += struct.pack("H", self.port) return r def __repr__(self): return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, self.ip, self.port) class CInv(object): typemap = { 0: "Error", 1: "TX", 2: "Block"} def __init__(self): self.type = 0 self.hash = 0L def deserialize(self, f): self.type = struct.unpack(" 21000000L * 100000000L: return False return True def __repr__(self): return "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i)" % (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime) class CBlock(object): def __init__(self): self.nVersion = 1 self.hashPrevBlock = 0 self.hashMerkleRoot = 0 self.nTime = 0 self.nBits = 0 self.nNonce = 0 self.vtx = [] self.sha256 = None self.hash = None def deserialize(self, f): self.nVersion = struct.unpack(" target: return False hashes = [] for tx in self.vtx: if not tx.is_valid(): return False tx.calc_sha256() hashes.append(ser_uint256(tx.sha256)) while len(hashes) > 1: newhashes = [] for i in xrange(0, len(hashes), 2): i2 = min(i+1, len(hashes)-1) newhashes.append(hash256(hashes[i] + hashes[i2])) hashes = newhashes if uint256_from_str(hashes[0]) != self.hashMerkleRoot: return False return True def __repr__(self): return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) class CUnsignedAlert(object): def __init__(self): self.nVersion = 1 self.nRelayUntil = 0 self.nExpiration = 0 self.nID = 0 self.nCancel = 0 self.setCancel = [] self.nMinVer = 0 self.nMaxVer = 0 self.setSubVer = [] self.nPriority = 0 self.strComment = "" self.strStatusBar = "" self.strReserved = "" def deserialize(self, f): self.nVersion = struct.unpack("= 106: self.addrFrom = CAddress() self.addrFrom.deserialize(f) self.nNonce = struct.unpack("= 209: self.nStartingHeight = struct.unpack(" 0) def handle_write(self): try: sent = self.send(self.sendbuf) except: self.handle_close() return self.sendbuf = self.sendbuf[sent:] def got_data(self): while True: if len(self.recvbuf) < 4: return if self.recvbuf[:4] != self.MAGIC_BYTES[settings['network']]: raise ValueError("got garbage %s" % repr(self.recvbuf)) if self.ver_recv < 209: if len(self.recvbuf) < 4 + 12 + 4: return command = self.recvbuf[4:4+12].split("\x00", 1)[0] msglen = struct.unpack("= 209: th = sha256(data) h = sha256(th) tmsg += h[:4] tmsg += data self.sendbuf += tmsg self.last_sent = time.time() def got_message(self, message): if self.last_sent + 30 * 60 < time.time(): self.send_message(msg_ping()) show_debug_msg("Recv %s" % repr(message)) if message.command == "version": if message.nVersion >= 209: self.send_message(msg_verack()) self.ver_send = min(MY_VERSION, message.nVersion) if message.nVersion < 209: self.ver_recv = self.ver_send elif message.command == "verack": self.ver_recv = self.ver_send elif message.command == "inv": want = msg_getdata() for i in message.inv: if i.type == 1: want.inv.append(i) elif i.type == 2: want.inv.append(i) if len(want.inv): self.send_message(want) elif message.command == "tx": new_transaction_event(message.tx) elif message.command == "block": new_block_event(message.block) if __name__ == '__main__': if len(sys.argv) == 2: f = open(sys.argv[1]) for line in f: m = re.search('^(\w+)\s*=\s*(\S.*)$', line) if m is None: continue settings[m.group(1)] = m.group(2) f.close() settings['port'] = int(settings['port']) c = NodeConn(settings['host'], settings['port']) asyncore.loop()