def save(self): self.verify() for txn in self.transactions: if txn.inputs: address = str( P2PKHBitcoinAddress.from_pubkey( txn.public_key.decode('hex'))) unspent = BU.get_wallet_unspent_transactions( self.config, self.mongo, address, [x.id for x in txn.inputs]) unspent_ids = [x['id'] for x in unspent] failed = False used_ids_in_this_txn = [] for x in txn.inputs: if x.id not in unspent_ids: failed = True if x.id in used_ids_in_this_txn: failed = True used_ids_in_this_txn.append(x.id) if failed: raise BaseException('double spend', [x.id for x in txn.inputs]) res = self.mongo.db.blocks.find({"index": (int(self.index) - 1)}) if res.count() and res[0]['hash'] == self.prev_hash or self.index == 0: self.mongo.db.blocks.insert(self.to_dict()) else: print "CRITICAL: block rejected..."
def verify(self): super(FastGraph, self).verify() result = self.mongo.db.fastgraph_transactions.find_one({ 'txn.hash': self.hash }) if not self.signatures: raise InvalidFastGraphTransactionException('no signatures were provided') xaddress = str(P2PKHBitcoinAddress.from_pubkey(self.public_key.decode('hex'))) unspent = [x['id'] for x in BU.get_wallet_unspent_transactions(self.config, self.mongo, xaddress)] unspent_fastgraph = [x['id'] for x in BU.get_wallet_unspent_fastgraph_transactions(self.config, self.mongo, xaddress)] inputs = [x.id for x in self.inputs] if len(set(inputs) & set(unspent)) != len(inputs) and len(set(inputs) & set(unspent_fastgraph)) != len(inputs): raise InvalidFastGraphTransactionException('Input not found in unspent') txn_for_rids = self.get_origin_relationship() if not txn_for_rids: raise InvalidFastGraphTransactionException('no origin transactions found') public_key = txn_for_rids['public_key'] for signature in self.signatures: signature.passed = False signed = verify_signature( base64.b64decode(signature.signature), self.hash, public_key.decode('hex') ) if signed: signature.passed = True """ # This is for a later fork to include a wider consensus area for a larger spending group else: mutual_friends = [x for x in BU.get_transactions_by_rid(self.config, self.mongo, self.rid, self.config.bulletin_secret, raw=True, rid=True, lt_block_height=highest_height)] for mutual_friend in mutual_friends: mutual_friend = Transaction.from_dict(self.config, self.mongo, mutual_friend) if isinstance(mutual_friend.relationship, Relationship) and signature.bulletin_secret == mutual_friend.relationship.their_bulletin_secret: other_mutual_friend = mutual_friend for mutual_friend in mutual_friends: mutual_friend = Transaction.from_dict(self.config, self.mongo, mutual_friend) if mutual_friend.public_key != self.config.public_key: identity = verify_signature( base64.b64decode(other_mutual_friend.relationship.their_bulletin_secret), other_mutual_friend.relationship.their_username, mutual_friend.public_key.decode('hex') ) signed = verify_signature( base64.b64decode(signature.signature), self.hash, mutual_friend.public_key.decode('hex') ) if identity and signed: signature.passed = True """ for signature in self.signatures: if not signature.passed: raise InvalidFastGraphTransactionException('not all signatures verified')
def do_money(self): my_address = str(P2PKHBitcoinAddress.from_pubkey(self.public_key.decode('hex'))) input_txns = BU.get_wallet_unspent_transactions(self.config, self.mongo, my_address) miner_transactions = self.mongo.db.miner_transactions.find() mtxn_ids = [] for mtxn in miner_transactions: for mtxninput in mtxn['inputs']: mtxn_ids.append(mtxninput['id']) inputs = self.inputs or [Input.from_dict(input_txn) for input_txn in input_txns if input_txn['id'] not in mtxn_ids] input_sum = 0 if self.coinbase: self.inputs = [] else: if inputs: needed_inputs = [] done = False for y in inputs: print y.id txn = BU.get_transaction_by_id(self.config, self.mongo, y.id, instance=True) for txn_output in txn.outputs: if txn_output.to == my_address: input_sum += txn_output.value needed_inputs.append(y) if input_sum >= (sum([x.value for x in self.outputs])+self.fee): done = True break if done == True: break if not done: raise NotEnoughMoneyException('not enough money') self.inputs = needed_inputs else: self.inputs = [] remainder = input_sum-(sum([x.value for x in self.outputs])+self.fee) found = False for x in self.outputs: if my_address == x.to: found = True x.value += remainder if not found: return_change_output = Output( to=my_address, value=remainder ) self.outputs.append(return_change_output)
def __init__(self, transactions, public_key, private_key, version, index=None, force_time=None): self.version = version if force_time: self.time = str(int(force_time)) else: self.time = str(int(time.time())) blocks = BU.get_blocks() self.index = index if self.index == 0: self.prev_hash = '' else: self.prev_hash = blocks[blocks.count()-1]['hash'] self.public_key = public_key self.private_key = private_key transaction_objs = [] fee_sum = 0.0 unspent_indexed = {} used_sigs = [] for txn in transactions: if isinstance(txn, Transaction): transaction_obj = txn else: transaction_obj = Transaction.from_dict(txn) if transaction_obj.transaction_signature in used_sigs: print 'duplicate transaction found and removed' continue used_sigs.append(transaction_obj.transaction_signature) transaction_obj.verify() #check double spend address = str(P2PKHBitcoinAddress.from_pubkey(transaction_obj.public_key.decode('hex'))) if address in unspent_indexed: unspent_ids = unspent_indexed[address] else: res = BU.get_wallet_unspent_transactions(address) unspent_ids = [x['id'] for x in res] unspent_indexed[address] = unspent_ids failed = False used_ids_in_this_txn = [] for x in transaction_obj.inputs: if x.id not in unspent_ids: failed = True if x.id in used_ids_in_this_txn: failed = True used_ids_in_this_txn.append(x.id) if not failed: transaction_objs.append(transaction_obj) fee_sum += float(transaction_obj.fee) block_reward = BU.get_block_reward() coinbase_txn_fctry = TransactionFactory( public_key=self.public_key, private_key=self.private_key, outputs=[Output( value=block_reward + float(fee_sum), to=str(P2PKHBitcoinAddress.from_pubkey(self.public_key.decode('hex'))) )], coinbase=True ) coinbase_txn = coinbase_txn_fctry.generate_transaction() transaction_objs.append(coinbase_txn) self.transactions = transaction_objs txn_hashes = self.get_transaction_hashes() self.set_merkle_root(txn_hashes) self.block = Block( version=self.version, block_time=self.time, block_index=self.index, prev_hash=self.prev_hash, transactions=self.transactions, merkle_root=self.merkle_root, public_key=self.public_key )
def __init__(self, config, mongo, transactions, public_key, private_key, version, index=None, force_time=None): self.config = config self.mongo = mongo self.version = BU.get_version_for_height(index) if force_time: self.time = str(int(force_time)) else: self.time = str(int(time.time())) blocks = BU.get_blocks(self.config, self.mongo) self.index = index if self.index == 0: self.prev_hash = '' else: self.prev_hash = BU.get_latest_block(self.config, self.mongo)['hash'] self.public_key = public_key self.private_key = private_key transaction_objs = [] fee_sum = 0.0 unspent_indexed = {} unspent_fastgraph_indexed = {} used_sigs = [] for txn in transactions: try: if isinstance(txn, Transaction): transaction_obj = txn else: transaction_obj = Transaction.from_dict( self.config, self.mongo, txn) if transaction_obj.transaction_signature in used_sigs: print 'duplicate transaction found and removed' continue used_sigs.append(transaction_obj.transaction_signature) transaction_obj.verify() if not isinstance(transaction_obj, FastGraph) and transaction_obj.rid: for input_id in transaction_obj.inputs: input_block = BU.get_transaction_by_id(self.config, self.mongo, input_id.id, give_block=True) if input_block['index'] > (BU.get_latest_block( self.config, self.mongo)['index'] - 2016): continue except: try: if isinstance(txn, FastGraph): transaction_obj = txn else: transaction_obj = FastGraph(**txn) if transaction_obj.transaction.transaction_signature in used_sigs: print 'duplicate transaction found and removed' continue used_sigs.append( transaction_obj.transaction.transaction_signature) if not transaction_obj.verify(): raise InvalidTransactionException( "invalid transactions") transaction_obj = transaction_obj.transaction except: raise InvalidTransactionException("invalid transactions") address = str( P2PKHBitcoinAddress.from_pubkey( transaction_obj.public_key.decode('hex'))) #check double spend if address in unspent_indexed: unspent_ids = unspent_indexed[address] else: res = BU.get_wallet_unspent_transactions( self.config, self.mongo, address) unspent_ids = [x['id'] for x in res] unspent_indexed[address] = unspent_ids if address in unspent_fastgraph_indexed: unspent_fastgraph_ids = unspent_fastgraph_indexed[address] else: res = BU.get_wallet_unspent_fastgraph_transactions( self.config, self.mongo, address) unspent_fastgraph_ids = [x['id'] for x in res] unspent_fastgraph_indexed[address] = unspent_fastgraph_ids failed = False used_ids_in_this_txn = [] for x in transaction_obj.inputs: if x.id not in unspent_ids: failed = True if x.id in used_ids_in_this_txn: failed = True used_ids_in_this_txn.append(x.id) if not failed: transaction_objs.append(transaction_obj) fee_sum += float(transaction_obj.fee) block_reward = BU.get_block_reward(self.config, self.mongo) coinbase_txn_fctry = TransactionFactory( config, mongo, public_key=self.public_key, private_key=self.private_key, outputs=[ Output(value=block_reward + float(fee_sum), to=str( P2PKHBitcoinAddress.from_pubkey( self.public_key.decode('hex')))) ], coinbase=True) coinbase_txn = coinbase_txn_fctry.generate_transaction() transaction_objs.append(coinbase_txn) self.transactions = transaction_objs txn_hashes = self.get_transaction_hashes() self.set_merkle_root(txn_hashes) self.block = Block(self.config, self.mongo, version=self.version, block_time=self.time, block_index=self.index, prev_hash=self.prev_hash, transactions=self.transactions, merkle_root=self.merkle_root, public_key=self.public_key)
def get_pending_transactions(self): transaction_objs = [] unspent_indexed = {} used_sigs = [] for txn in self.combine_transaction_lists(): try: if isinstance(txn, FastGraph) and hasattr(txn, 'signatures'): transaction_obj = txn elif isinstance(txn, Transaction): transaction_obj = txn elif isinstance(txn, dict) and 'signatures' in txn: transaction_obj = FastGraph.from_dict(self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)['index'], txn) elif isinstance(txn, dict): transaction_obj = Transaction.from_dict(self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)['index'], txn) else: print 'transaction unrecognizable, skipping' continue if transaction_obj.transaction_signature in used_sigs: print 'duplicate transaction found and removed' continue used_sigs.append(transaction_obj.transaction_signature) transaction_obj.verify() if not isinstance(transaction_obj, FastGraph) and transaction_obj.rid: for input_id in transaction_obj.inputs: input_block = BU.get_transaction_by_id(self.config, self.mongo, input_id.id, give_block=True) if input_block and input_block['index'] > (BU.get_latest_block(self.config, self.mongo)['index'] - 2016): continue #check double spend address = str(P2PKHBitcoinAddress.from_pubkey(transaction_obj.public_key.decode('hex'))) if address in unspent_indexed: unspent_ids = unspent_indexed[address] else: needed_value = sum([float(x.value) for x in transaction_obj.outputs]) + float(transaction_obj.fee) res = BU.get_wallet_unspent_transactions(self.config, self.mongo, address, needed_value=needed_value) unspent_ids = [x['id'] for x in res] unspent_indexed[address] = unspent_ids failed1 = False failed2 = False used_ids_in_this_txn = [] for x in transaction_obj.inputs: if x.id not in unspent_ids: failed1 = True if x.id in used_ids_in_this_txn: failed2 = True used_ids_in_this_txn.append(x.id) if failed1: self.mongo.db.miner_transactions.remove({'id': transaction_obj.transaction_signature}) print 'transaction removed: input presumably spent already, not in unspent outputs', transaction_obj.transaction_signature self.mongo.db.failed_transactions.insert({'reason': 'input presumably spent already', 'txn': transaction_obj.to_dict()}) elif failed2: self.mongo.db.miner_transactions.remove({'id': transaction_obj.transaction_signature}) print 'transaction removed: using an input used by another transaction in this block', transaction_obj.transaction_signature self.mongo.db.failed_transactions.insert({'reason': 'using an input used by another transaction in this block', 'txn': transaction_obj.to_dict()}) else: transaction_objs.append(transaction_obj) except MissingInputTransactionException as e: #print 'missing this input transaction, will try again later' pass except InvalidTransactionSignatureException as e: print 'InvalidTransactionSignatureException: transaction removed' self.mongo.db.miner_transactions.remove({'id': transaction_obj.transaction_signature}) self.mongo.db.failed_transactions.insert({'reason': 'InvalidTransactionSignatureException', 'txn': transaction_obj.to_dict()}) except InvalidTransactionException as e: print 'InvalidTransactionException: transaction removed' self.mongo.db.miner_transactions.remove({'id': transaction_obj.transaction_signature}) self.mongo.db.failed_transactions.insert({'reason': 'InvalidTransactionException', 'txn': transaction_obj.to_dict()}) except Exception as e: print e #print 'rejected transaction', txn['id'] pass except BaseException as e: print e #print 'rejected transaction', txn['id'] pass return transaction_objs