def get_shared_secrets_by_rid(cls, config, mongo, rid): from blockchainutils import BU shared_secrets = [] dh_public_keys = [] dh_private_keys = [] txns = BU.get_transactions_by_rid(config, mongo, rid, config.bulletin_secret, rid=True) for txn in txns: if str(txn['public_key']) == str( config.public_key ) and txn['relationship']['dh_private_key']: dh_private_keys.append(txn['relationship']['dh_private_key']) txns = BU.get_transactions_by_rid(config, mongo, rid, config.bulletin_secret, rid=True, raw=True) for txn in txns: if str(txn['public_key']) != str( config.public_key) and txn['dh_public_key']: dh_public_keys.append(txn['dh_public_key']) for dh_public_key in dh_public_keys: for dh_private_key in dh_private_keys: shared_secrets.append( scalarmult(dh_private_key.decode('hex'), dh_public_key.decode('hex'))) return shared_secrets
def sync_bottom_up(self): #bottom up syncing self.latest_block = Block.from_dict( self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)) self.remove_pending_transactions_now_in_chain() self.remove_fastgraph_transactions_now_in_chain() latest_consensus = self.mongo.db.consensus.find_one( {'index': self.latest_block.index + 1}) if latest_consensus: latest_consensus = Block.from_dict(self.config, self.mongo, latest_consensus['block']) print latest_consensus.index, "latest consensus_block" records = self.mongo.db.consensus.find({ 'index': self.latest_block.index + 1, 'block.version': BU.get_version_for_height(self.latest_block.index + 1), 'ignore': { '$ne': True } }) for record in sorted(records, key=lambda x: int(x['block']['target'], 16)): self.import_block(record) break else: self.log('up to date, height: ' + str(self.latest_block.index)) return
def refresh(self): Peers.init(self.config, self.mongo, self.config.network) block = BU.get_latest_block(self.config, self.mongo) if block: block = Block.from_dict(self.config, self.mongo, block) self.height = block.index + 1 else: genesis_block = BlockFactory.get_genesis_block(self.config, self.mongo) genesis_block.save() self.mongo.db.consensus.insert({ 'block': genesis_block.to_dict(), 'peer': 'me', 'id': genesis_block.signature, 'index': 0 }) block = Block.from_dict(self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)) self.height = block.index try: self.block_factory = BlockFactory( config=self.config, mongo=self.mongo, transactions=self.get_pending_transactions(), public_key=self.config.public_key, private_key=self.config.private_key, index=self.height, version=BU.get_version_for_height(self.height)) self.set_target(int(self.block_factory.block.time)) if not self.block_factory.block.special_min: self.set_target_from_last_non_special_min(block) self.block_factory.block.header = BlockFactory.generate_header(self.block_factory.block) except Exception as e: raise e
def get_origin_relationship(self, rid=None, bulletin_secret=None): for inp in self.inputs: inp = inp.id while 1: txn = BU.get_transaction_by_id(self.config, self.mongo, inp, give_block=False, include_fastgraph=True) if txn: if 'rid' in txn and txn['rid'] and 'dh_public_key' in txn and txn['dh_public_key']: if rid and txn['rid'] != rid: continue rids = [txn['rid']] if 'requester_rid' in txn and txn['requester_rid']: rids.append(txn['requester_rid']) if 'requested_rid' in txn and txn['requested_rid']: rids.append(txn['requested_rid']) # we need their public_key, not mine, so we get both transactions for the relationship txn_for_rids = BU.get_transaction_by_rid(self.config, self.mongo, rids, bulletin_secret=bulletin_secret, raw=True, rid=True, theirs=True, public_key=self.public_key) if txn_for_rids: return txn_for_rids else: return False else: inp = txn['inputs'][0]['id'] else: txn = self.mongo.db.fastgraph_transactions.find_one({'id': inp}) if txn and 'inputs' in txn['txn'] and txn['txn']['inputs'] and 'id' in txn['txn']['inputs'][0]: inp = txn['txn']['inputs'][0]['id'] else: return False
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 refresh(self): Peers.init(self.config, self.mongo, self.config.network) if self.config.network == 'mainnet': max_block_time = 600 elif self.config.network == 'testnet': max_block_time = 10 block = BU.get_latest_block(self.config, self.mongo) if block: block = Block.from_dict(self.config, self.mongo, block) self.height = block.index + 1 else: genesis_block = BlockFactory.get_genesis_block( self.config, self.mongo) genesis_block.save() self.mongo.db.consensus.insert({ 'block': genesis_block.to_dict(), 'peer': 'me', 'id': genesis_block.signature, 'index': 0 }) block = Block.from_dict( self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)) self.height = block.index try: if self.height > 0: last_time = block.time special_min = False max_target = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff if self.height > 0: time_elapsed_since_last_block = int( time.time()) - int(last_time) # special min case if time_elapsed_since_last_block > max_block_time: target = max_target special_min = True self.target = BlockFactory.get_target( self.config, self.mongo, self.height, last_time, block, Blockchain(self.config, self.mongo, [x for x in BU.get_blocks(self.config, self.mongo)])) self.block_factory = BlockFactory( config=self.config, mongo=self.mongo, transactions=self.get_pending_transactions(), public_key=self.config.public_key, private_key=self.config.private_key, index=self.height, version=BU.get_version_for_height(self.height)) self.block_factory.block.special_min = special_min self.block_factory.block.target = self.target self.block_factory.header = BlockFactory.generate_header( self.block_factory.block) except Exception as e: raise
def get_lookup_rids(self): lookup_rids = [ self.rid, ] lookup_rids.extend( [x['rid'] for x in BU.get_friend_requests(self.rid)]) lookup_rids.extend( [x['rid'] for x in BU.get_sent_friend_requests(self.rid)]) return list(set(lookup_rids))
def verify(self): getcontext().prec = 8 if int(self.version) != int(BU.get_version_for_height(self.index)): raise BaseException("Wrong version for block height", self.version, BU.get_version_for_height(self.index)) try: txns = self.get_transaction_hashes() self.set_merkle_root(txns) if self.verify_merkle_root != self.merkle_root: raise BaseException("Invalid block") except: raise try: header = BlockFactory.generate_header(self) hashtest = BlockFactory.generate_hash_from_header( header, str(self.nonce)) if self.hash != hashtest: raise BaseException('Invalid block') except: raise address = P2PKHBitcoinAddress.from_pubkey( self.public_key.decode('hex')) try: result = verify_signature(base64.b64decode(self.signature), self.hash, self.public_key.decode('hex')) if not result: raise Exception("block signature is invalid") except: try: result = VerifyMessage(address, BitcoinMessage(self.hash, magic=''), self.signature) if not result: raise except: raise BaseException("block signature is invalid") # verify reward coinbase_sum = 0 for txn in self.transactions: if txn.coinbase: for output in txn.outputs: coinbase_sum += float(output.value) fee_sum = 0.0 for txn in self.transactions: if not txn.coinbase: fee_sum += float(txn.fee) reward = BU.get_block_reward(self.config, self.mongo, self) if Decimal(str(fee_sum)[:10]) != (Decimal(str(coinbase_sum)[:10]) - Decimal(str(reward)[:10])): raise BaseException( "Coinbase output total does not equal block reward + transaction fees", fee_sum, (coinbase_sum - reward))
def run(cls, config, mongo): used_inputs = [] new_inputs = [] for x in mongo.site_db.faucet.find({'active': True}): balance = BU.get_wallet_balance(config, mongo, x['address']) if balance >= 25: mongo.site_db.faucet.update({'_id': x['_id']}, { 'active': False, 'address': x['address'] }) continue last_id_in_blockchain = x.get('last_id') if last_id_in_blockchain and not mongo.db.blocks.find({ 'transactions.id': last_id_in_blockchain }).count(): continue try: transaction = TransactionFactory( config, mongo, block_height=BU.get_latest_block(config, mongo)['index'], fee=0.01, public_key=config.public_key, private_key=config.private_key, outputs=[Output(to=x['address'], value=5)]) except NotEnoughMoneyException as e: print "not enough money yet" return except Exception as e: print x try: transaction.transaction.verify() except: mongo.site_db.failed_faucet_transactions.insert( transaction.transaction.to_dict()) print 'faucet transaction failed' TU.save(config, mongo, transaction.transaction) x['last_id'] = transaction.transaction.transaction_signature mongo.site_db.faucet.update({'_id': x['_id']}, x) print 'saved. sending...', x['address'] for peer in Peers.peers: try: socketIO = SocketIO(peer.host, peer.port, wait_for_connection=False) chat_namespace = socketIO.define(ChatNamespace, '/chat') chat_namespace.emit('newtransaction', transaction.transaction.to_dict()) socketIO.disconnect() except Exception as e: print e
def __init__(self, config, mongo, debug=False): self.debug = debug self.config = config self.mongo = mongo latest_block = BU.get_latest_block(self.config, self.mongo) if latest_block: self.latest_block = Block.from_dict(self.config, self.mongo, latest_block) else: self.insert_genesis() self.existing_blockchain = Blockchain(self.config, self.mongo, BU.get_blocks(self.config, self.mongo))
def with_private_key(self): all_relationships = [x for x in BU.get_transactions() if x['rid']] self.rid_usernames = dict( (x['rid'], x['relationship']['their_username']) for x in all_relationships) rids = [x['rid'] for x in all_relationships] self.rid_transactions = BU.get_transactions_by_rid(rids, rid=True, raw=True, returnheight=True)
def get_request_rids_for_rid(self): lookup_rids = {} for x in BU.get_friend_requests(self.rid): if x['rid'] not in lookup_rids: lookup_rids[x['rid']] = [] lookup_rids[x['rid']].append(x['requester_rid']) for x in BU.get_sent_friend_requests(self.rid): if x['rid'] not in lookup_rids: lookup_rids[x['rid']] = [] lookup_rids[x['rid']].append(x['requested_rid']) return lookup_rids
def get_next_consensus_block_from_local(self, block): #table cleanup new_block = self.mongo.db.consensus.find_one({ 'block.prevHash': block.hash, 'block.index': (block.index + 1), 'block.version': BU.get_version_for_height((block.index + 1)) }) if new_block: new_block = Block.from_dict(self.config, self.mongo, new_block['block']) if int(new_block.version) == BU.get_version_for_height(new_block.index): return new_block else: return None return None
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 get_previous_consensus_block_from_local(self, block, peer): #table cleanup new_block = self.mongo.db.consensus.find_one({ 'block.hash': block.prev_hash, 'block.index': (block.index - 1), 'block.version': BU.get_version_for_height((block.index - 1)), 'ignore': {'$ne': True} }) if new_block: new_block = Block.from_dict(self.config, self.mongo, new_block['block']) if int(new_block.version) == BU.get_version_for_height(new_block.index): return new_block else: return None return None
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 get_posts(self): if self.wallet_mode: self.posts = [] return my_bulletin_secret = Config.get_bulletin_secret() posts = [] blocked = [ x['username'] for x in Mongo.db.blocked_users.find( {'bulletin_secret': self.bulletin_secret}) ] flagged = [ x['id'] for x in Mongo.db.flagged_content.find( {'bulletin_secret': self.bulletin_secret}) ] for x in BU.get_posts(self.rid): rids = sorted( [str(my_bulletin_secret), str(x.get('bulletin_secret'))], key=str.lower) rid = hashlib.sha256(str(rids[0]) + str(rids[1])).digest().encode('hex') res = Mongo.site_db.usernames.find({'rid': rid}, {'_id': 0}) if res.count(): x['username'] = res[0]['username'] if x['username'] not in blocked and x['id'] not in flagged: posts.append(x) self.posts = posts
def get_latest_consensus_block(self): latests = self.get_latest_consensus_blocks() for latest in latests: if int(latest['block']['version']) == BU.get_version_for_height( latest['block']['index']): return Block.from_dict(self.config, self.mongo, latest['block'])
def get_posts(self): if self.wallet_mode: self.posts = [] return my_bulletin_secret = self.config.bulletin_secret posts = [] blocked = [ x['username'] for x in self.mongo.db.blocked_users.find( {'bulletin_secret': self.bulletin_secret}) ] flagged = [ x['id'] for x in self.mongo.db.flagged_content.find( {'bulletin_secret': self.bulletin_secret}) ] for x in BU.get_posts(self.config, self.mongo, self.rid): rids = sorted( [str(my_bulletin_secret), str(x.get('bulletin_secret'))], key=str.lower) rid = hashlib.sha256(str(rids[0]) + str(rids[1])).digest().encode('hex') if rid in self.rid_usernames: x['username'] = self.rid_usernames[rid] if x['username'] not in blocked and x['id'] not in flagged: posts.append(x) self.posts = posts
def get_previous_consensus_block_from_remote(self, block, peer): retry = 0 while True: try: url = 'http://' + peer.to_string( ) + '/get-block?hash=' + block.prev_hash print 'getting block', url res = requests.get(url, timeout=1, headers={'Connection': 'close'}) except: if retry == 50: raise BadPeerException() else: retry += 1 continue try: print 'response code: ', res.status_code new_block = Block.from_dict(self.config, self.mongo, json.loads(res.content)) if int(new_block.version) == BU.get_version_for_height( new_block.index): return new_block else: return None except: return None
def get_latest_consensus_blocks(self): for x in self.mongo.db.consensus.find({}, { '_id': 0 }).sort([('index', -1)]): if BU.get_version_for_height(x['block']['index']) == int( x['block']['version']): yield x
def get_input_hashes(self): from fastgraph import FastGraph input_hashes = [] for x in self.inputs: txn = BU.get_transaction_by_id(self.config, self.mongo, x.id, instance=True, include_fastgraph=isinstance( self, FastGraph)) if txn: input_hashes.append(str(txn.transaction_signature)) else: found = False if self.extra_blocks: for block in self.extra_blocks: for xtxn in block.transactions: if xtxn.transaction_signature == x.id: input_hashes.append( str(xtxn.transaction_signature)) found = True break if found: break if not found: raise MissingInputTransactionException( "This transaction is not in the blockchain.") return ''.join(sorted(input_hashes, key=lambda v: v.lower()))
def get_input_hashes(self): input_hashes = [] for x in self.inputs: txn = BU.get_transaction_by_id(self.config, self.mongo, x.id, instance=True) input_hashes.append(str(txn.transaction_signature)) return ''.join(sorted(input_hashes, key=str.lower))
def nonce_generator(self): latest_block_index = BU.get_latest_block(self.config, self.mongo)['index'] while 1: next_latest_block_index = BU.get_latest_block( self.config, self.mongo)['index'] if latest_block_index < next_latest_block_index: latest_block_index = next_latest_block_index start_nonce = 0 self.refresh() else: try: start_nonce += 1000000 except: start_nonce = 0 self.index = latest_block_index yield [start_nonce, start_nonce + 1000000]
def get_consensus_blocks_by_index(self, index): return self.mongo.db.consensus.find( { 'index': index, 'block.prevHash': { '$ne': '' }, 'block.version': BU.get_version_for_height(index) }, {'_id': 0})
def verify(self): txn = BU.get_transaction_by_id(self.config, self.mongo, self.id, instance=True) result = verify_signature(base64.b64decode(self.signature), self.id, txn.public_key.decode('hex')) if not result: raise Exception('Invalid external input')
def get_input_hashes(self): input_hashes = [] for x in self.inputs: txn = BU.get_transaction_by_id(self.config, self.mongo, x.id, instance=True) if not txn: raise MissingInputTransactionException("This transaction is not in the blockchain.") input_hashes.append(str(txn.transaction_signature)) return ''.join(sorted(input_hashes, key=lambda v: v.lower()))
def sync_bottom_up(self): #bottom up syncing last_latest = self.latest_block self.latest_block = Block.from_dict(self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)) if self.latest_block.index > last_latest.index: print 'Block height: %s | time: %s' % (self.latest_block.index, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) self.remove_pending_transactions_now_in_chain() self.remove_fastgraph_transactions_now_in_chain() latest_consensus = self.mongo.db.consensus.find_one({ 'index': self.latest_block.index + 1, 'block.version': BU.get_version_for_height(self.latest_block.index + 1), 'ignore': {'$ne': True} }) if latest_consensus: latest_consensus = Block.from_dict(self.config, self.mongo, latest_consensus['block']) if self.debug: print latest_consensus.index, "latest consensus_block" records = self.mongo.db.consensus.find({ 'index': self.latest_block.index + 1, 'block.version': BU.get_version_for_height(self.latest_block.index + 1), 'ignore': {'$ne': True} }) for record in sorted(records, key=lambda x: int(x['block']['target'], 16)): result = self.import_block(record) last_latest = self.latest_block self.latest_block = Block.from_dict(self.config, self.mongo, BU.get_latest_block(self.config, self.mongo)) if self.latest_block.index > last_latest.index: print 'Block height: %s | time: %s' % (self.latest_block.index, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) latest_consensus_now = self.mongo.db.consensus.find_one({ 'index': self.latest_block.index + 1, 'block.version': BU.get_version_for_height(self.latest_block.index + 1), 'ignore': {'$ne': True} }) if latest_consensus_now and latest_consensus.index == latest_consensus_now['index']: self.search_network_for_new() return True else: self.search_network_for_new() return True
def __init__(self, config, mongo): self.config = config self.mongo = mongo latest_block = BU.get_latest_block(self.config, self.mongo) if latest_block: self.latest_block = Block.from_dict(self.config, self.mongo, latest_block) else: self.insert_genesis() blocks = self.mongo.db.blocks.find({}) self.existing_blockchain = Blockchain(self.config, self.mongo, blocks)
def broadcast_block(self, block): Peers.init(self.config, self.mongo, self.config.network) dup_test = self.mongo.db.consensus.find_one({ 'peer': 'me', 'index': block.index, 'block.version': BU.get_version_for_height(block.index) }) if not dup_test: print '\r\nCandidate submitted for index:', block.index print '\r\nTransactions:' for x in block.transactions: print x.transaction_signature self.mongo.db.consensus.insert({ 'peer': 'me', 'index': block.index, 'id': block.signature, 'block': block.to_dict() }) print '\r\nSent block to:' for peer in Peers.peers: if peer.is_me: continue try: block_dict = block.to_dict() block_dict['peer'] = Peers.my_peer requests.post('http://{peer}/newblock'.format( peer=peer.host + ":" + str(peer.port)), json=block_dict, timeout=3, headers={'Connection': 'close'}) print peer.host + ":" + str(peer.port) except Exception as e: print e try: print 'reporting bad peer' if self.config.network == 'mainnet': url = 'https://yadacoin.io/peers' elif self.config.network == 'testnet': url = 'http://yadacoin.io:8888/peers' requests.post(url, json={ 'host': peer.host, 'port': str(peer.port), 'failed': True }, timeout=3, headers={'Connection': 'close'}) except: print 'failed to report bad peer' pass