+++ /dev/null
-import bitcoin
-import threading
-import time
-
-class ExpiryQueue(threading.Thread):
-
- def __init__(self):
- self.lock = threading.Lock()
- self.items = []
- threading.Thread.__init__(self)
- self.daemon = True
-
- def run(self):
- # Garbage collection
- while True:
- with self.lock:
- self.items = [i for i in self.items if not i.stopped()]
- time.sleep(0.1)
-
- def add(self, item):
- with self.lock:
- self.items.append(item)
-
-expiry_queue = ExpiryQueue()
-
-class StatementLine:
-
- def __init__(self, output_point):
- self.lock = threading.Lock()
- self.output_point = output_point
- self.output_loaded = None
- self.input_point = None
- self.input_loaded = None
- self.raw_output_script = None
-
- def is_loaded(self):
- with self.lock:
- if self.output_loaded is None:
- return False
- elif (self.input_point is not False and
- self.input_loaded is None):
- return False
- return True
-
-class PaymentHistory:
-
- def __init__(self, chain):
- self.chain = chain
- self.lock = threading.Lock()
- self.statement = []
- self._stopped = False
-
- def run(self, address, handle_finish):
- self.address = address
- self.handle_finish = handle_finish
-
- pubkey_hash = bitcoin.address_to_short_hash(address)
- self.chain.fetch_outputs(pubkey_hash, self.start_loading)
-
- def start_loading(self, ec, output_points):
- with self.lock:
- for outpoint in output_points:
- statement_line = StatementLine(outpoint)
- self.statement.append(statement_line)
- self.chain.fetch_spend(outpoint,
- bitcoin.bind(self.load_spend,
- bitcoin._1, bitcoin._2, statement_line))
- self.load_tx_info(outpoint, statement_line, False)
-
- def load_spend(self, ec, inpoint, statement_line):
- with statement_line.lock:
- if ec:
- statement_line.input_point = False
- else:
- statement_line.input_point = inpoint
- self.finish_if_done()
- if not ec:
- self.load_tx_info(inpoint, statement_line, True)
-
- def finish_if_done(self):
- with self.lock:
- if any(not line.is_loaded() for line in self.statement):
- return
- result = []
- for line in self.statement:
- if line.input_point:
- line.input_loaded["value"] = -line.output_loaded["value"]
- result.append(line.input_loaded)
- else:
- line.output_loaded["raw_output_script"] = \
- line.raw_output_script
- result.append(line.output_loaded)
- self.handle_finish(result)
- self.stop()
-
- def stop(self):
- with self.lock:
- self._stopped = True
-
- def stopped(self):
- with self.lock:
- return self._stopped
-
- def load_tx_info(self, point, statement_line, is_input):
- info = {}
- info["tx_hash"] = str(point.hash)
- info["index"] = point.index
- info["is_input"] = 1 if is_input else 0
- self.chain.fetch_transaction_index(point.hash,
- bitcoin.bind(self.tx_index, bitcoin._1, bitcoin._2, bitcoin._3,
- statement_line, info))
-
- def tx_index(self, ec, block_depth, offset, statement_line, info):
- info["height"] = block_depth
- self.chain.fetch_block_header_by_depth(block_depth,
- bitcoin.bind(self.block_header, bitcoin._1, bitcoin._2,
- statement_line, info))
-
- def block_header(self, ec, blk_head, statement_line, info):
- info["timestamp"] = blk_head.timestamp
- info["block_hash"] = str(bitcoin.hash_block_header(blk_head))
- tx_hash = bitcoin.hash_digest(info["tx_hash"])
- self.chain.fetch_transaction(tx_hash,
- bitcoin.bind(self.load_tx, bitcoin._1, bitcoin._2,
- statement_line, info))
-
- def load_tx(self, ec, tx, statement_line, info):
- outputs = []
- for tx_out in tx.outputs:
- script = tx_out.output_script
- if script.type() == bitcoin.payment_type.pubkey_hash:
- pkh = bitcoin.short_hash(str(script.operations()[2].data))
- outputs.append(bitcoin.public_key_hash_to_address(pkh))
- else:
- outputs.append("Unknown")
- info["outputs"] = outputs
- info["inputs"] = [None for i in range(len(tx.inputs))]
- if info["is_input"] == 1:
- info["inputs"][info["index"]] = self.address
- else:
- our_output = tx.outputs[info["index"]]
- info["value"] = our_output.value
- with statement_line.lock:
- statement_line.raw_output_script = \
- str(bitcoin.save_script(our_output.output_script))
- if not [empty_in for empty_in in info["inputs"] if empty_in is None]:
- # We have the sole input
- assert(info["is_input"] == 1)
- with statement_line.lock:
- statement_line.input_loaded = info
- self.finish_if_done()
- for tx_idx, tx_in in enumerate(tx.inputs):
- if info["is_input"] == 1 and info["index"] == tx_idx:
- continue
- self.chain.fetch_transaction(tx_in.previous_output.hash,
- bitcoin.bind(self.load_input, bitcoin._1, bitcoin._2,
- tx_in.previous_output.index, statement_line, info, tx_idx))
-
- def load_input(self, ec, tx, index, statement_line, info, inputs_index):
- script = tx.outputs[index].output_script
- if script.type() == bitcoin.payment_type.pubkey_hash:
- pkh = bitcoin.short_hash(str(script.operations()[2].data))
- info["inputs"][inputs_index] = \
- bitcoin.public_key_hash_to_address(pkh)
- else:
- info["inputs"][inputs_index] = "Unknown"
- if not [empty_in for empty_in in info["inputs"] if empty_in is None]:
- with statement_line.lock:
- if info["is_input"] == 1:
- statement_line.input_loaded = info
- else:
- statement_line.output_loaded = info
- self.finish_if_done()
-
-def payment_history(chain, address, handle_finish):
- ph = PaymentHistory(chain)
- expiry_queue.add(ph)
- ph.run(address, handle_finish)
-
-if __name__ == "__main__":
- def finish(result):
- print result
- def last(ec, depth):
- print "D:", depth
-
- service = bitcoin.async_service(1)
- prefix = "/home/genjix/libbitcoin/database"
- chain = bitcoin.bdb_blockchain(service, prefix)
- chain.fetch_last_depth(last)
- address = "1Pbn3DLXfjqF1fFV9YPdvpvyzejZwkHhZE"
- print "Looking up", address
- payment_history(chain, address, finish)
- raw_input()
-