class UTXOSet(): db_file = "utxo.db" utxo_bucket = "utxo" def __init__(self, blockchain): self._bucket = Bucket(UTXOSet.db_file, UTXOSet.utxo_bucket) self._bc = blockchain def reindex(self): self._bucket.reset() utxos = self._bc.find_utxo() for txid, outs in utxos.items(): self._bucket.put(txid, utils.serialize(outs)) self._bucket.save() def find_spendable_outputs(self, pubkey_hash, amount): account_amount = 0 unspent_outputs = defaultdict(list) for tx_id, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out_idx, out in enumerate(outs): if out.is_locked_with_key( pubkey_hash) and account_amount < amount: account_amount += out.value unspent_outputs[tx_id].append(out_idx) return account_amount, unspent_outputs def update(self, block): for tx in block.transactions: if not isinstance(tx, CoinbaseTx): for vin in tx.vin: update_outs = [] outs_bytes = self._bucket.get(vin.txid) outs = utils.deserialize(outs_bytes) for out_idx, out in enumerate(outs): if out_idx != vin.vout: update_outs.append(out) if len(update_outs) == 0: self._bucket.delete(vin.txid) else: self._bucket.put(vin.txid, utils.serialize(update_outs)) # Add new outputs new_outputs = [out for out in tx.vout] self._bucket.put(tx.id, utils.serialize(new_outputs)) self._bucket.save() def print_utxo(self): utxos = [] for _, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out in outs: print(out.value) def find_utxo(self, pubkey_hash): utxos = [] for _, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out in outs: if out.is_locked_with_key(pubkey_hash): utxos.append(out) return utxos @property def blockchain(self): return self._bc
class BlockChain(object): bucket = 'blocks' db_file = 'block_chain.db' genesis_block_data = 'This is a Genesis block!' def __init__(self, address=None): self._bucket = Bucket(BlockChain.db_file, BlockChain.bucket) try: self._address = self._bucket.get('address') self._last_hash = self._bucket.get('l') self._last_block = pickle.loads(self._bucket.get(self._last_hash)) except KeyError: if not address: # no data & no given address print('Block Chain not created yet!\nPlease create a new block chain with given address first!') sys.exit() else: self.reset(address) def _put_block(self, block): self._last_block = block self._last_hash = block.hash self._bucket.put('l', block.hash) self._bucket.put(block.hash, block.serialize()) self._bucket.commit() def add_block(self, transactions): ''' Args: transactions (list): List of transactions ''' current_height = self._last_block.height new_block = Block(current_height+1, self._last_hash, transactions) new_block.set_hash() self._put_block(new_block) def reset(self, address): self._bucket.reset() coinbaseTx = CoinBaseTx(address) genesis_block = Block(0, '', [coinbaseTx]) genesis_block.set_hash() self._put_block(genesis_block) self._address = address self._last_block = genesis_block self._last_hash = genesis_block.hash self._bucket.put('address', address) self._bucket.commit() return self @property def blocks(self): current_hash = self._last_hash while current_hash: encoded_block = self._bucket.get(current_hash) block = pickle.loads(encoded_block) yield block current_hash = block.prev_hash @property def address(self): return self._address @property def last_block(self): return self._last_block @property def last_hash(self): return self._last_hash def print_all_blocks(self): for block in self.blocks: block.print_block() def print_block_with_height(self, height): for block in self.blocks: if block.height == height: block.print_block() return print('No block with height {} found!'.format(height)) def find_all_utxo(self): utxo = defaultdict(list) stxo = defaultdict(list) for block in self.blocks: for tx in block.transactions: try: for out_idx, out in enumerate(tx.vout): if stxo[tx.id]: for spent_out in stxo[tx.id]: if spent_out == out_idx: raise utils.ContinueIt utxo[tx.id].append(out) except utils.ContinueIt: pass if not isinstance(tx, CoinBaseTx): for vin in tx.vin: stxo[tx.id].append(vin.vout_idx) return utxo
class UTXOSet(object): db_file = 'blockchain.db' utxo_bucket = 'utxo' def __init__(self, blockchain): self._bucket = Bucket(UTXOSet.db_file, UTXOSet.utxo_bucket) self._bc = blockchain @property def blockchain(self): return self._bc def reindex(self): # Rebuilds the UTXO set self._bucket.reset() utxos = self._bc.find_utxo() for tx_id, outs in utxos.items(): self._bucket.put(tx_id, utils.serialize(outs)) self._bucket.commit() def find_spendable_outputs(self, pubkey_hash, amount): # Finds and returns unspent outputs to reference in inputs accumulated = 0 unspent_outputs = defaultdict(list) for tx_id, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out_idx, out in enumerate(outs): if out.is_locked_with_key(pubkey_hash) and accumulated < amount: accumulated += out.value unspent_outputs[tx_id].append(out_idx) return accumulated, unspent_outputs def find_utxo(self, pubkey_hash): # Finds UTXO for a public key hash utxos = [] for _, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out in outs: if out.is_locked_with_key(pubkey_hash): utxos.append(out) return utxos def print_utxo(self): utxos = [] for _, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out in outs: print(out) def count_transactions(self): # Returns the number of transactions in the UTXO set return len(self._bucket) def update(self, block): # Updates the UTXO set with transactions from the Block for tx in block.transactions: if not isinstance(tx, CoinbaseTx): for vin in tx.vin: update_outs = [] outs_bytes = self._bucket.get(vin.tx_id) outs = utils.deserialize(outs_bytes) for out_idx, out in enumerate(outs): if out_idx != vin.vout: update_outs.append(out) if len(update_outs) == 0: self._bucket.delete(vin.tx_id) else: self._bucket.put( vin.tx_id, utils.serialize(update_outs)) # Add new outputs new_outputs = [out for out in tx.vout] self._bucket.put(tx.ID, utils.serialize(new_outputs)) self._bucket.commit() @property def utxo_set(self): return {k: utils.deserialize(v) for k, v in self._bucket.kv.items()}
class UTXOSet(object): ''' Maintain all UTXOs in current blockchain ''' db_file = 'block_chain.db' bucket = 'utxo' def __init__(self, block_chain): self._bucket = Bucket(UTXOSet.db_file, UTXOSet.bucket) self._bc = block_chain def reset(self): self._bucket = Bucket(UTXOSet.db_file, UTXOSet.bucket) self._bucket.reset() utxos = self._bc.find_all_utxo() for tx_id, vout_idx in utxos.items(): self._bucket.put(tx_id, utils.serialize(vout_idx)) self._bucket.commit() def update(self, block): self._bucket = Bucket(UTXOSet.db_file, UTXOSet.bucket) transactions = block.transactions for tx in transactions: if not isinstance(tx, CoinBaseTx): for vin in tx.vin: update_outs = [] encoded_outs = self._bucket.get(vin.txid) outs = utils.deserialize(encoded_outs) for out_idx, out in enumerate(outs): if out_idx != vin.vout_idx: update_outs.append(out) if len(update_outs) == 0: self._bucket.delete(vin.txid) else: self._bucket.put( vin.txid, utils.serialize(update_outs)) new_output = [out for out in tx.vout] self._bucket.put(tx.id, utils.serialize(new_output)) self._bucket.commit() def find_spendable_output(self, address, amount): accumulate = 0 spendable_output = defaultdict(list) for tx_id, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out_idx, out in enumerate(outs): if out.address == address: accumulate += out.value spendable_output[tx_id].append(out_idx) if accumulate >= amount: return accumulate, spendable_output return accumulate, spendable_output def find_utxo_by_address(self, address): accumulate = 0 utxos = defaultdict(list) for tx_id, outs in self._bucket.kv.items(): outs = utils.deserialize(outs) for out_idx, out in enumerate(outs): if out.address == address: accumulate += out.value utxos[tx_id].append(out_idx) return accumulate, utxos