Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

playing around

  • Loading branch information...
commit 2d782ad7530d0a47af03da5d0f097dea754e0703 1 parent 3d6d1e9
Jonas Rudloff authored
View
282 blockchain.py
@@ -6,13 +6,23 @@
import bsddb
import logging
+import database
+from database import txn_required
log = logging.getLogger("pycoin.blockchain")
-
+class BlockError(Exception):
+ pass
+class TxError(Exception):
+ pass
+class TxInputAlreadySpend(TxError):
+ pass
+
MAIN_CHAIN = 1
SIDE_CHAIN = 2
ORPHAN_CHAIN = 3
+INVALID_CHAIN = 4
+_chains = {MAIN_CHAIN: "Main", SIDE_CHAIN: "Side", ORPHAN_CHAIN: "Orphan", INVALID_CHAIN: "Invalid"}
class BlockAux(js.Entity, bs.Entity):
fields = {
"block": msgs.Block,
@@ -35,14 +45,20 @@ def chain_block(self, next_aux):
assert self.hash == next_aux.prev
next_aux.number = self.number + 1
next_aux.totaldiff = self.totaldiff + bits_to_diff(next_aux.block.bits)
- self.next += [next_aux.hash]
+ self.next.append(next_aux.hash)
@property
def hash(self):
return self.block.hash
+
@property
def prev(self):
- return self.block.prev
+ return self.block.prev
+
+ def __repr__(self):
+ return "<Block %s(%d) - diff: %d - chain: %s>" % (
+ h2h(self.hash), self.number, bits_to_diff(self.block.bits), _chains.get(self.chain, "unknown"))
+
@constructor
def make(self, blockmsg):
self.block, self.txs = blockmsg.block, [tx.hash for tx in blockmsg.txs]
@@ -53,57 +69,87 @@ class TxAux(js.Entity, bs.Entity):
"tx":msgs.Tx,
"block":js.Hash,
"redeemed":js.List(js.Hash),
+ "blkindex":js.Int
}
bfields = [
("tx", msgs.Tx),
("block", bs.Hash),
("redeemed", bs.VarList(bs.Hash)),
+ ("blkindex", bs.structfmt("<L")),
]
@property
def hash(self):
return self.tx.hash
+
@property
def confirmed(self):
return self.block != nullhash
+
@property
def coinbase(self):
return self.tx.inputs[0].outpoint.tx == nullhash
+
+ @property
+ def total_amount_spend(self):
+ return sum([i.amount for i in self.tx.inputs])
+
@constructor
def make(self, tx):
self.tx, self.block = tx, nullhash
+ self.blkindex = 0
self.redeemed = [nullhash] * len(tx.outputs)
-
-def set_chain(block, chain_type, tx_func=None):
- block.chain = chain_type
- if tx_func:
- for tx in block.txs:
- tx_func(tx)
-
+
+ def get_output(self, outpoint):
+ assert self.hash == outpoint.tx, "outpoint hash does not point to tx"
+ assert outpoint.index < len(self.tx.outputs), "outpoint index out of range"
+ return self.tx.outputs[outpoint.index]
+
+ def spend_output(self, outpoint, tx):
+ assert self.hash == outpoint.tx, "outpoint hash does not point to tx"
+ assert outpoint.index < len(self.tx.outputs), "outpoint index out of range"
+ if self.redeemed[outpoint.index] != nullhash:
+ raise TxInputAlreadySpend(outpoint)
+ self.redeemed[outpoint.index] = tx.hash
+
+ def unspend_output(self, outpoint):
+ assert self.hash == outpoint.tx, "outpoint hash does not point to tx"
+ assert outpoint.index < len(self.tx.outputs), "outpoint index out of range"
+ self.redeemed[outpoint.index] = nullhash
+
class BlockChain(object):
def __init__(self):
- self.chain = bsddb.btopen("chain.dat")
- self.txs = bsddb.btopen("txs.dat")
+ self.chain = database.open_db("chain.dat")
+ self.txs = database.open_db("txs.dat")
+ self.state = database.open_db("state.dat")
+ self.blknums = database.open_db("blknum.dat")
self.orphans = {}
- if genesisblock.hash not in self.chain:
+ if not self.chain.has_key(genesisblock.hash):
self.add_genesis(genesisblock.blockmsg)
- def get_aux(self, h):
+ def get_aux(self, h, txn=None):
"""get a Block Aux in the database"""
- log.debug("getting %s", h2h(h))
- return BlockAux.frombinary(self.chain[h])[0]
- def put_aux(self, aux):
+ log.debug("getting block %s", h2h(h))
+ return BlockAux.frombinary(self.chain.get(h, txn=txn))[0]
+
+ @txn_required
+ def put_aux(self, aux, txn=None):
"""get a Block Aux in the database"""
- log.debug("putting %s", h2h(aux.hash))
- self.chain[aux.hash] = aux.tobinary()
-
+ log.debug("putting block %s", h2h(aux.hash))
+ self.chain.put(aux.hash, aux.tobinary(), txn=txn)
+ if aux.chain == MAIN_CHAIN:
+ self.blknums.put(str(aux.number), aux.hash, txn=txn)
+
+ def get_aux_by_num(self, idx, txn=None):
+ return self.get_aux(self.blknums.get(str(idx), txn=txn), txn=txn)
+
@property
def missing_blocks(self):
missing = self.orphans.keys()
return [h for h in missing if not self.has_block(h)]
- def has_block(self, h):
- if h in self.chain:
+ def has_block(self, h, txn=None):
+ if self.chain.has_key(h):
return True
if h in map(lambda o: o.hash, sum(self.orphans.values(), [])): # hack: sum([["a", "b"], ["c"]], []) = ["a", "b", "c"]
return True
@@ -112,79 +158,164 @@ def has_block(self, h):
def has_tx(self, h):
return h in self.txs
- def get_tx(self, h):
+ def get_tx(self, h, txn=None):
return TxAux.frombinary(self.txs[h])[0]
- def put_tx(self, tx):
+ def put_tx(self, tx, txn=None):
self.txs[tx.hash] = tx.tobinary()
- @property
- def bestblock(self):
- """a property that holdes the bestblock. when set, it dicides if the new block is better then the old one.
- it performs a reorg if the new block's prev is not the old block"""
- return self.get_aux(self.chain["bestblock"])
-
- @bestblock.setter
- def bestblock(self, new):
- old = self.bestblock
+ def get_bestblock(self, txn=None):
+ return self.get_aux(self.state.get("bestblock", txn=txn), txn=txn)
+
+ def get_target(self, prev_aux, txn=None):
+ targettimespan = 14 * 24 * 60 * 60 # 2 weeks in secounds
+ spacing = 10 * 60 # 10 min in secounds
+ interval = timespan/spacing
+ if (prev_aux.number + 1) % interval != 0:
+ return prev_aux.block.bits
+ log.info("DIFF: retarget, current bits: %s", hex(prev_aux.block.bits))
+ first_aux = prev_aux
+ for i in range(interval-1):
+ first_aux = self.get_aux(first_aux.prev, txn=txn)
+ realtimespan = last_aux.block.time - first_aux.block.time
+ log.info("DIFF: timespan before limits: %d", realtimespan)
+ if realtimespan < targettimespan/4:
+ realtimespan = targettimespan/4
+ if realtimespan > targettimespan*4:
+ realtimespan = targettimespan*4
+ log.info("DIFF: timespan after limits: %d", realtimespan)
+ newtarget = (bits_to_target(prev_aux.block.bits)*realtimespan)/targettimespan
+ return target_to_bits(newtarget)
+
+ @txn_required
+ def put_bestblock(self, new, txn=None):
+ old = self.get_bestblock(txn=txn)
if old.totaldiff < new.totaldiff:
if new.prev != old.hash:
- self.reorg(old, new)
+ self.reorg(old, new, txn=txn)
new.chain = MAIN_CHAIN
- self.put_aux(new)
- self.chain["bestblock"] = new.hash
+ self.put_aux(new, txn=txn)
+ self.state.put("bestblock", new.hash, txn=txn)
+ if new.number % 2016 == 0:
+ self.diff_retarget(aux, txn=txn)
log.info("new best block %s(%d)", h2h(new.hash), new.number)
- #self.chain.sync()
else:
log.info("%s is not better then the currently best block %s", h2h(new.hash), h2h(old.hash))
- def findsplit(self, old, new):
+ def findsplit(self, old, new, txn=None):
"""finds the split between old and new.
Returns (spilt, old, new)"""
log.info("finding spilt for %s(%d) and %s(%d)", h2h(old.hash), old.number, h2h(new.hash), new.number)
+ oldlist = []
+ newlist = []
while old.number != new.number:
if old.number > new.number:
- old = self.get_aux(old.prev)
+ oldlist.append(old.hash)
+ old = self.get_aux(old.prev, txn=txn)
else:
- new = self.get_aux(new.prev)
+ newlist.append(new.hash)
+ new = self.get_aux(new.prev, txn=txn)
while old.prev != new.prev:
- old = self.get_aux(old.prev)
- new = self.get_aux(new.prev)
- split = self.get_aux(new.prev)
+ oldlist.append(old.hash)
+ newlist.append(new.hash)
+ old = self.get_aux(old.prev, txn=txn)
+ new = self.get_aux(new.prev, txn=txn)
+ split = self.get_aux(new.prev, txn=txn)
log.info("spilt found at %s(%d)", h2h(split.hash), split.number)
- return split, old, new
+ return split.hash, oldlist, newlist
- def for_all_in_chain(self, tail_block, head_block, block_cb):
- """performs func on all nexts in chain(First_block included)"""
- block = head_block
- while block != tail_block:
- block_cb(block)
- self.put_aux(block)
- block = self.get_aux(block.prev)
-
- def reorg(self, old, new):
+ def revert_tx(self, tx, coinbase=False, txn=None):
+ tx.block = nullhash
+ if coinbase:
+ return tx
+ for i in tx.tx.inputs:
+ input_tx = self.get_tx(i.outpoint.tx, txn=txn)
+ input_tx.redeemed[i.outpoint.index] = nullhash
+ self.put_tx(input_tx, txn=txn)
+ return tx
+
+ def verify_tx(self, tx, coinbase=False, txn=None):
+ pass
+
+ def comfirm_tx(self, tx, blk, coinbase=False, txn=None):
+ tx.block = blk.hash
+ if coinbase:
+ return tx
+ for i in tx.tx.inputs:
+ input_tx = self.get_tx(i.outpoint.tx, txn=txn)
+ if input_tx.redeemed[i.outpoint.index] != nullhash:
+ raise TxError("input already spend", tx, input_tx)
+ input_tx.redeemed[i.outpoint.index] = tx.hash
+ self.put_tx(input_tx, txn=txn)
+ return tx
+
+ def sum_tx_amount_input(self, tx, txn=None):
+ amount = 0
+ for inp in t.tx.inputs:
+ input_tx = self.get_tx(inp.outpoint.tx, txn=txn)
+ amount += input_tx.tx.outputs[inp.outpoint.index].amount
+
+ def sum_tx_amount_output(self, tx, txn=None):
+ return sum([outp.amount for outp in tx.tx.outputs])
+
+ def revert_block(self, aux, txn=None):
+ aux.chain = SIDE_CHAIN
+ self.state.put("bestblock", aux.prev, txn=txn)
+ tx = self.get_tx(aux.txs[0], txn=txn)
+ tx = self.revert_tx(tx, True, txn=txn)
+ self.put_tx(tx, txn=txn)
+ for tx_h in aux.txs[1:]:
+ tx = self.get_tx(tx_h, txn=txn)
+ tx = self.revert_tx(tx, txn=txn)
+ self.put_tx(tx, txn=txn)
+ return aux
+
+ def chain_block(self, aux, txn=None):
+ aux.chain = MAIN_CHAIN
+ self.state.put("bestblock", aux.hash, txn=txn)
+ tx = self.get_tx(aux.txs[0], txn=txn)
+ tx = self.comfirm_tx(tx, aux, True, txn=txn)
+ self.put_tx(tx, txn=txn)
+ for tx_h in aux.txs[1:]:
+ tx = self.get_tx(tx_h, txn=txn)
+ tx = self.comfirm_tx(tx, aux, txn=txn)
+ self.put_tx(tx, txn=txn)
+ return aux
+
+ def for_all_blocks(self, l, func, txn=None):
+ for h in l:
+ aux = self.get_aux(h, txn=txn)
+ aux = func(aux, txn=txn)
+ self.put_aux(aux, txn=txn)
+
+ @txn_required
+ def reorg(self, old, new, txn=None):
"""reorginizes the blockchain, so that the new block, is the main block. and all blocks in the old chain is invalidated."""
- #raise RuntimeError("reorg not implemented")
- split, oldhead, newhead = self.findsplit(old, new)
- self.for_all_in_chain(split, oldhead, lambda blk: set_chain(blk, SIDE_CHAIN, None))
- self.for_all_in_chain(split, newhead, lambda blk: set_chain(blk, MAIN_CHAIN, None))
+ log.info("REORG old: %s, new: %s", h2h(old.hash), h2h(new.hash))
+ split, oldchain, newchain = self.findsplit(old, new, txn=txn)
+ log.info("blocks to revert: %s", ", ".join(map(h2h, oldchain)))
+ log.info("blocks to chain: %s", ", ".join(map(h2h, newchain)))
+ self.for_all_blocks(oldchain, self.revert_block, txn=txn)
+ self.for_all_blocks(newchain[::-1], self.chain_block, txn=txn)
+ log.info("REORG done.")
def add_genesis(self, blockmsg):
"""inits the block chain, by putting in the genesis block"""
aux = BlockAux.make(blockmsg)
aux.totaldiff = 1
aux.number = 0
+ aux.chain = MAIN_CHAIN
self.put_aux(aux)
- self.chain["bestblock"] = aux.hash
+ self.state.put("bestblock", aux.hash)
def verify_block(self, aux):
return True
- def check_orphans(self, aux):
+ def check_orphans(self, aux, txn=None):
blocks = self.orphans.pop(aux.hash, [])
while blocks:
aux = blocks.pop(0)
- self._add_block(aux, False)
+ self._add_block(aux, False, txn=txn)
blocks += self.orphans.pop(aux.hash, [])
def add_orphan(self, aux):
@@ -195,26 +326,28 @@ def add_orphan(self, aux):
def add_tx(self, tx):
tx = TxAux.make(tx)
self.put_tx(tx)
-
- def add_block(self, blockmsg):
+
+ @txn_required
+ def add_block(self, blockmsg, txn=None):
aux = BlockAux.make(blockmsg)
- if self.has_block(aux.hash):
- log.info("already having %s", h2h(aux.hash))
- return
for tx in blockmsg.txs:
self.add_tx(tx)
- self._add_block(aux)
+ if self.has_block(aux.hash, txn=txn):
+ log.info("already having block %s", h2h(aux.hash))
+ return
+ self._add_block(aux, txn=txn)
+
- def _add_block(self, aux, check_orphans=True):
+ def _add_block(self, aux, check_orphans=True, txn=None):
log.info("chaining %s to chain", h2h(aux.hash))
- if aux.prev in self.chain:
- aux_prev = self.get_aux(aux.prev)
+ if self.chain.has_key(aux.prev, txn=txn):
+ aux_prev = self.get_aux(aux.prev, txn=txn)
aux_prev.chain_block(aux)
- self.put_aux(aux_prev)
- self.put_aux(aux)
- self.bestblock = aux
+ self.put_aux(aux_prev, txn=txn)
+ self.put_aux(aux, txn=txn)
+ self.put_bestblock(aux, txn=txn)
if check_orphans:
- self.check_orphans(aux)
+ self.check_orphans(aux, txn=txn)
else:
log.info("%s is missing %s", h2h(aux.hash), h2h(aux.prev))
self.add_orphan(aux)
@@ -222,6 +355,7 @@ def _add_block(self, aux, check_orphans=True):
def test():
import struct
import sys
+ logging.basicConfig(format='%(name)s - %(message)s', level=logging.INFO)
blkfile = open(sys.argv[1],"r")
chain = BlockChain()
while True:
@@ -231,6 +365,6 @@ def test():
break
blkdata = blkfile.read(size)
blkmsg = msgs.Blockmsg.frombinary(blkdata)[0]
- chain.add(blkmsg)
+ chain.add_block(blkmsg)
if __name__ == "__main__":
test()
View
93 database.py
@@ -0,0 +1,93 @@
+from bsddb import db as DB
+from time import sleep
+import collections
+import logging
+log = logging.getLogger("pycoin.database")
+
+
+homedir="./db"
+envflags = [DB.DB_THREAD, DB.DB_CREATE, DB.DB_INIT_MPOOL, DB.DB_INIT_LOCK, DB.DB_INIT_LOG, DB.DB_INIT_TXN, DB.DB_RECOVER]
+dbflags = [DB.DB_THREAD, DB.DB_AUTO_COMMIT, DB.DB_CREATE]
+
+to_int = lambda l: reduce(lambda x, y: x|y, l)
+
+env = DB.DBEnv()
+env.open(homedir, to_int(envflags))
+log.info("env opened")
+
+def open_db(filename, dbtype=DB.DB_BTREE, flags=[]):
+ db = DB.DB(env)
+ if not flags:
+ flags = dbflags
+ db.open(filename, dbtype, to_int(flags))
+ log.info("database %s opened", filename)
+ return db
+
+def run_in_transaction(func, *args, **kwargs):
+ i = 10
+ sleeptime = 0.01
+ while True:
+ txn = env.txn_begin(flags=DB.DB_TXN_NOWAIT)
+ print "TXN BEGIN", txn
+ kwargs["txn"] = txn
+ try:
+ return func(*args, **kwargs)
+ except DB.DBError:
+ print "TXN ABORT", txn
+ _txn, txn = txn, None
+ _txn.abort()
+
+ if i <= 0:
+ raise
+ i -= 1
+ print "Deadlock: sleeping %1.2f sec" % sleeptime
+ sleep(sleeptime)
+ sleeptime *= 2
+ except Exception as e:
+ print "TXN ERROR(%s)"%repr(e), txn
+ _txn, txn = txn, None
+ _txn.abort()
+ raise
+ finally:
+ print "TXN END", txn
+ if txn:
+ txn.commit()
+
+def Transaction(func):
+ def _func(*args, **kwargs):
+ return run_in_transaction(func, *args, **kwargs)
+ return _func
+
+def txn_required(func):
+ def _func(*args, **kwargs):
+ txn = kwargs.get("txn", None)
+ if txn:
+ return func(*args, **kwargs)
+ else:
+ return run_in_transaction(func, *args, **kwargs)
+ return _func
+
+class dictdb(collections.MutableMapping):
+ def __init__(self, db, txn=None):
+ self.db = db
+ self.txn = txn
+ def __getitem__(self, key):
+ item = self.db.get(key, txn=self.txn)
+ if item == None:
+ raise KeyError
+ else:
+ return item
+ def __setitem__(self, key, item):
+ self.db.put(key, item, txn=self.txn)
+ def __delitem__(self, key):
+ self.db.delete(key, txn=self.txn)
+ def __contains__(self, key):
+ return self.db.exists(key, txn=self.txn)
+ def __iter__(self):
+ cur = self.db.cursor(txn=self.txn)
+ key = cur.first()
+ while key:
+ yield key[0]
+ key = cur.next()
+ def __len__(self):
+ return self.db.stat()["nkeys"]
View
28 debug.py
@@ -0,0 +1,28 @@
+import signal, code, traceback
+
+def signal_handler( signal_number ):
+ """
+ A decorator to set the specified function as handler for a signal.
+ This function is the 'outer' decorator, called with only the (non-function)
+ arguments
+ """
+
+ # create the 'real' decorator which takes only a function as an argument
+ def __decorator( function ):
+ signal.signal( signal_number, function )
+ return function
+
+ return __decorator
+
+@signal_handler(signal.SIGINT)
+def debug_handler(sig, frame):
+ """Interrupt running process, and provide a python prompt for
+ interactive debugging."""
+ d={'_frame':frame} # Allow access to frame object.
+ d.update(frame.f_globals) # Unless shadowed by global
+ d.update(frame.f_locals)
+
+ i = code.InteractiveConsole(d)
+ message = "Signal recieved : entering python shell.\nTraceback:\n"
+ message += ''.join(traceback.format_stack(frame))
+ i.interact(message)
View
27 msgs.py
@@ -18,7 +18,7 @@
class Header():
def __init__(self, data, magic=MAGIC_MAINNET):
- self.magic, self.cmd, self.len = struct.unpack("<L12sL", data)
+ self.magic, self.cmd, self.len, self.cksum = struct.unpack("<L12sL4s", data)
if self.magic != magic:
raise ProtocolViolation("wrong magic") # Client shutdown
self.cmd = self.cmd.strip("\x00")
@@ -27,21 +27,14 @@ def __init__(self, data, magic=MAGIC_MAINNET):
except KeyError:
print "??",self.cmd
raise ProtocolViolation # Unrecognized message type
- if self.cmd not in ("version", "verack"):
- self.len += 4
def deserialize(self, data):
- if self.cmd not in ("version", "verack"):
- self.cksum, data = data[:4], data[4:]
- if self.cksum != checksum(data):
- raise ProtocolViolation
+ if self.cksum != checksum(data):
+ raise ProtocolViolation
return self.type.frombinary(data)[0]
@staticmethod
def serialize(msg, magic=MAGIC_MAINNET):
data = msg.tobinary()
- if msg.type not in ("version", "verack"):
- return struct.pack("<L12sL4s", magic, msg.type.encode('ascii'), len(data), checksum(data)[:4]) + data
- else: # No checksum in version and verack
- return struct.pack("<L12sL", magic, msg.type.encode('ascii'), len(data)) + data
+ return struct.pack("<L12sL4s", magic, msg.type.encode('ascii'), len(data), checksum(data)[:4]) + data
class Address(js.Entity, bs.Entity):
fields = {
@@ -70,7 +63,7 @@ class Version(js.Entity, bs.Entity):
"reciever":Address,
"sender":Address,
"nonce":js.Int,
- "subverinfo":js.Str,
+ "useragent":js.Str,
"finalblock":js.Int
}
bfields = [
@@ -80,18 +73,18 @@ class Version(js.Entity, bs.Entity):
("reciever", Address),
("sender", Address),
("nonce", bs.structfmt("<Q")),
- ("subverinfo", bs.Str),
+ ("useragent", bs.Str),
("finalblock", bs.structfmt("<I")),
]
@constructor
- def make(self, version=31900, sender=Address.make("0.0.0.0",0), reciever=Address.make("0.0.0.0",0)):
+ def make(self, version=50000, sender=Address.make("0.0.0.0",0), reciever=Address.make("0.0.0.0",0), useragent="pycoin"):
self.version = version
self.services = 0
self.time = int(time.time())
self.sender = sender
self.reciever = reciever
self.nonce = 1234134124
- self.subverinfo = ""
+ self.useragent = useragent
self.finalblock = 1
class Verack(js.Entity, bs.Entity):
@@ -249,6 +242,10 @@ class Tx(js.Entity, bs.Entity):
@cachedproperty
def hash(self):
return doublesha(self.tobinary())
+ @property
+ def coinbase(self):
+ return len(self.inputs) == 1 and self.inputs[0].outpoint.tx == nullhash and self.inputs[0].outpoint.index == 0xffffffff
+
class Block(js.Entity, bs.Entity):
fields = {
View
26 network.py
@@ -11,7 +11,7 @@
import bsddb
import random
import logging
-import debug
+#import debug
LOG = logging.getLogger("pycoin.network")
class NodeDisconnected(Exception):
@@ -43,7 +43,7 @@ def init(self):
peer_addr = self.socket.getpeername()
except:
self.close("never connected")
- self.peer_address = msgs.Address.make(peer_addr[0], peer_addr[1])
+ self.peer_address = msgs.Address.make(*peer_addr)
self.connected = True
self.on_init()
@@ -72,9 +72,9 @@ def _read(self):
self.close("peer shutdown")
self.inbuf += data
if not self.hdr:
- if len(self.inbuf) >= 20:
+ if len(self.inbuf) >= 24:
try:
- self.hdr,self.inbuf = msgs.Header(self.inbuf[:20]), self.inbuf[20:]
+ self.hdr,self.inbuf = msgs.Header(self.inbuf[:24]), self.inbuf[24:]
except ProtocolViolation as e:
self.close(repr(e))
else:
@@ -109,7 +109,7 @@ def __init__(self, server=None, *args, **kwargs):
Node.__init__(self, *args, **kwargs)
self.server = server
self.active = False
- self.peer_address = msgs.Address.make("0.0.0.0", 8333)
+ self.peer_address = msgs.Address.make("0.0.0.0",8333)
self.sendmsg(msgs.Version.make(sender=self.server.address, reciever=self.peer_address))
def on_init(self):
@@ -122,7 +122,10 @@ def handle_version(self, msg):
if msg.version < 31900:
self.close("to low version %d" % msg.version)
self.sendmsg(msgs.Verack.make())
-
+
+ def __repr__(self):
+ return "<Node %s:%d - %s>" %(self.peer_address.ip, self.peer_address.port, self.active and "Active" or "Inactive")
+
def handle_verack(self, msg):
self.active = True
self.server.node_connected(self)
@@ -138,7 +141,7 @@ def __init__(self, listen_sock=None, hosts=[], txs={}, chain=None):
self.listeners = []
self.addrs = set()
self.timers = timerq.Timerq()
- self.address = msgs.Address.make("127.0.0.1",8333)
+ self.address = msgs.Address.make("127.0.0.1",833)
for h in map(lambda h: (h, 8333), hosts):
self.check_host(h)
self.timers.add_event(15, self.search_for_missing_blocks)
@@ -207,11 +210,8 @@ def broadcast(self, msg):
node.sendmsg(msg)
def handle_inv(self, node, msg):
- for i in msg.objs:
- if i.objtype == msgs.TYPE_BLOCK:
- if self.chain.has_block(i.hash):
- continue
- self.sendrandom(msgs.Getdata.make([i]))
+ objs = [obj for obj in msg.objs if obj.objtype == msgs.TYPE_BLOCK and not self.chain.has_block(obj.hash)]
+ node.sendmsg(msgs.Getdata.make(objs))
def sendrandom(self, msg):
active_nodes = filter(lambda n: n.active, self.nodes)
@@ -219,7 +219,7 @@ def sendrandom(self, msg):
LOG.debug("no nodes connected, not sending %s", msg.type)
return
node = random.choice(active_nodes)
- LOG.debug("sending a %s to %s", msg.type, node.peer_address.ip)
+ LOG.debug("sending a %s to %s", msg.type, repr(node))
node.sendmsg(msg)
def handle_block(self, node, msg):
View
36 pycoin.py
@@ -4,33 +4,25 @@
import blockchain
import code
import logging
-logging.basicConfig(format='%(name)s - %(message)s', level=logging.INFO)
+import socket
+import sys
+
+#logging.basicConfig(format='%(name)s - %(message)s', level=logging.INFO)
chain = blockchain.BlockChain()
server = network.BitcoinServer(hosts=["127.0.0.1"], chain=chain)
server_thread = threading.Thread(target=server.serve_forever)
-server_thread.run()
-
-class SocketConsole(code.InteractiveConsole):
- def __init__(self, locals, socket):
- code.InteractiveConsole.__init__(self, locals)
- self._file = socket.makefile("rw")
- def raw_input(p=""):
- self._file.write(p)
- return self._file.readline()
+server_thread.daemon = True
+server_thread.start()
-def console_thread(s):
+def console():
d = {"chain":chain, "server":server}
- console = SocketConsole(d, s)
+ console = code.InteractiveConsole(d)
console.interact()
+
+console_thread = threading.Thread(target=console)
+console_thread.daemon = True
+console_thread.start()
+
+console_thread.join()
-sock = socket.socket()
-sock.bind("127.0.0.1", 7777)
-sock.listen(5)
-while True:
- try:
- s,a = sock.accept()
- th = threading.Thread(target=lambda : console_thread(s))
- th.run()
- except:
- break
View
73 script.py
@@ -1,73 +0,0 @@
-import struct
-import hashlib
-def hash160(d):
- sha256 = hashlib.sha256()
- sha256.update(d)
- ripemd160 = hashlib.new("ripemd160")
- ripemd160.update(sha256.digest())
- return ripemd160.digest()
-
-class InvalidScript(Exception):
- pass
-
-class Engine:
- def __init__(self, script=None):
- self.stack = []
- self.invalid = True
- self.alt_stack = []
- if script:
- self.eval(script)
- def push(self, i):
- self.stack.append(i)
- def pop(self):
- return self.stack.pop()
- def eval(self, script):
- while script != "":
- opcode, script = ord(script[0]), script[1:]
- if opcode == 0:
- self.push("\x00")
- elif 1 <= opcode <= 75:
- data, script = script[:opcode], script[opcode:]
- if len(data) != opcode:
- raise InvalidScript
- self.push(data)
- elif opcode == 76:
- length, script = struct.unpack("<B", script[0])[0], script[1:]
- data, script = script[:lenght], script[opcode:]
- if len(data) != lenght:
- raise InvalidScript
- elif opcode == 81:
- self.push("\x01")
- elif 82 <= opcode <= 96:
- self.push(opcode-80)
- elif opcode == 97:
- pass
- elif opcode == 105:
- i = bool(self.pop())
- self.invalid = not i
- elif opcode == 106:
- self.invalid = True
- elif opcode == 107:
- self.alt_stack.append(self.pop())
- elif opcode == 108:
- self.push(self.alt_stack.pop()
- elif opcode == 116:
- self.push(len(self.stack))
- elif opcode == 117:
- self.pop()
- elif opcode == 118:
- i = self.pop()
- self.push(i)
- self.push(i)
- elif opcode == 119:
- i = self.pop()
- self.pop()
- self.push(i)
- elif opcode == 120:
- i = self.pop()
- n = self.pop()
- self.push(n)
- self.push(i)
- self.push(n)
- elif opcode == 169:
- self.push(hash160(self.pop()))
View
0  transactions.py → tx_utils.py
File renamed without changes
View
6 utils.py
@@ -1,4 +1,5 @@
from hashlib import sha256
+import math
#import jserialize as js
class ProtocolViolation(Exception):
"""Indicates that the communication protocol between this and a remote
@@ -27,6 +28,11 @@ def f(self):
def bits_to_target(bits):
return (bits & 0x00ffffff) * 2 ** (8 * ((bits >> 24) - 3))
+def target_to_bits(target):
+ e = int(math.log(target, 2))/8 + 1
+ p = target >> 8*(e-3)
+ return (e << 24) + p
+
def bits_to_diff(bits):
return bits_to_target(0x1d00ffff) // bits_to_target(bits)
Please sign in to comment.
Something went wrong with that request. Please try again.