def get_fastgraph_transactions(self, secret, query, queryType, raw=False, both=True, skip=None): from yadacoin.crypt import Crypt cipher = Crypt(secret) for transaction in self.mongo.db.fastgraph_transactions.find(query): if 'txn' in transaction: try: if transaction.get('id') in skip: continue if 'relationship' not in transaction: continue if not transaction['relationship']: continue res = self.mongo.db.fastgraph_transaction_cache.find_one({ 'txn.id': transaction.get('id'), }) if res: continue if not raw: decrypted = cipher.decrypt(transaction['relationship']) relationship = json.loads(decrypted.decode('latin1')) transaction['relationship'] = relationship self.mongo.db.fastgraph_transaction_cache.update( { 'txn': transaction, } , upsert=True) except: continue for x in self.mongo.db.fastgraph_transaction_cache.find({ 'txn': {'$exists': True} }): yield x['tnx']
def get_relationships(self, wif): # from block import Block # from transaction import Transaction from yadacoin.crypt import Crypt relationships = [] for block in self.config.BU.get_blocks(): for transaction in block.get('transactions'): try: cipher = Crypt(wif) decrypted = cipher.decrypt(transaction['relationship']) relationship = json.loads(decrypted.decode('latin1')) relationships.append(relationship) except: continue return relationships
def get_transaction_by_rid(self, selector, wif=None, bulletin_secret=None, rid=False, raw=False, theirs=False, my=False, public_key=None): # from block import Block # from transaction import Transaction from yadacoin.crypt import Crypt if not rid: ds = bulletin_secret selectors = [ TU.hash(ds + selector), TU.hash(selector + ds) ] else: if not isinstance(selector, list): selectors = [selector, ] else: selectors = selector def txn_gen(): res = self.mongo.db.blocks.find( {"transactions": {"$elemMatch": {"relationship": {"$ne": ""}, "rid": {"$in": selectors}}}}) for x in res: yield x res = self.mongo.db.fastgraph_transactions.find( {"txn": {"$elemMatch": {"relationship": {"$ne": ""}, "rid": {"$in": selectors}}}}) for x in res: yield x for block in txn_gen(): for transaction in block.get('transactions'): if theirs and public_key == transaction['public_key']: continue if my and public_key != transaction['public_key']: continue if not raw: try: cipher = Crypt(wif) decrypted = cipher.decrypt(transaction['relationship']) relationship = json.loads(decrypted.decode('latin1')) transaction['relationship'] = relationship except: continue if 'rid' in transaction and transaction['rid'] in selectors: return transaction
def verify_message(self, rid, message, public_key, txn_id=None): from yadacoin.crypt import Crypt sent = False received = False res = self.mongo.db.verify_message_cache.find_one({ 'rid': rid, 'message.signIn': message.decode('utf-8') }) if res: received = True else: shared_secrets = self.get_shared_secrets_by_rid(rid) if txn_id: txns = [] txn = [self.config.BU.get_transaction_by_id(txn_id, include_fastgraph=True)] if txn: txns.append(txn) else: txns = [x for x in self.get_transactions_by_rid(rid, self.config.bulletin_secret, rid=True, raw=True)] fastgraph_transactions = self.mongo.db.fastgraph_transactions.find({"txn.rid": rid}) if fastgraph_transactions.count() > 0: txns.extend([x['txn'] for x in fastgraph_transactions]) for txn in txns: for shared_secret in list(set(shared_secrets)): res = self.mongo.db.verify_message_cache.find_one({ 'rid': rid, 'shared_secret': shared_secret.hex(), 'message': message.decode('utf-8'), 'id': txn['id'] }) try: if res and res['success']: signin = res['message'] elif res and not res['success']: continue else: cipher = Crypt(shared_secret.hex(), shared=True) try: decrypted = cipher.shared_decrypt(txn['relationship']) signin = json.loads(decrypted.decode('utf-8')) self.mongo.db.verify_message_cache.update({ 'rid': rid, 'shared_secret': shared_secret.hex(), 'id': txn['id'] }, { 'rid': rid, 'shared_secret': shared_secret.hex(), 'id': txn['id'], 'message': signin, 'success': True } , upsert=True) except: continue if u'signIn' in signin and message.decode('utf-8') == signin['signIn']: if public_key != txn['public_key']: received = True else: sent = True except: self.mongo.db.verify_message_cache.update({ 'rid': rid, 'shared_secret': shared_secret.hex(), 'id': txn['id'] }, { 'rid': rid, 'shared_secret': shared_secret.hex(), 'id': txn['id'], 'message': '', 'success': False } , upsert=True) return sent, received
def get_posts(self, rids): from yadacoin.crypt import Crypt if not isinstance(rids, list): rids = [rids, ] posts_cache = self.mongo.db.posts_cache.find({ 'rid': {'$in': rids} }).sort([('height', -1)]) latest_block = self.config.BU.get_latest_block() if posts_cache.count(): posts_cache = posts_cache[0] block_height = posts_cache['height'] else: block_height = 0 transactions = self.mongo.db.blocks.aggregate([ { "$match": { "index": {'$gt': block_height} } }, { "$match": { "transactions": {"$elemMatch": {"relationship": {"$ne": ""}}}, "transactions.dh_public_key": '', "transactions.rid": '' } }, {"$unwind": "$transactions"}, { "$project": { "_id": 0, "txn": "$transactions", "height": "$index" } }, { "$match": { "txn.relationship": {"$ne": ""}, "txn.dh_public_key": '', "txn.rid": '' } }, { "$sort": {"height": 1} } ]) fastgraph_transactions = self.mongo.db.fastgraph_transactions.find({ "txn.relationship": {"$ne": ""}, "txn.dh_public_key": '', "txn.rid": '' }) transactions = [x for x in transactions] + [x for x in fastgraph_transactions] # transactions are all posts not yet cached by this rid # so we want to grab all bulletin secrets for this rid mutual_bulletin_secrets = self.get_mutual_bulletin_secrets(rids) friends = [] for friend in self.get_transactions_by_rid(rids, self.config.bulletin_secret, rid=True): if 'their_bulletin_secret' in friend['relationship']: friends.append(friend['relationship']['their_bulletin_secret']) friends = list(set(friends)) had_txns = False if friends: mutual_bulletin_secrets.extend(friends) for i, x in enumerate(transactions): res = self.mongo.db.posts_cache.find_one({ 'rid': {'$in': rids}, 'id': x['txn']['id'] }) if res: continue for bs in mutual_bulletin_secrets: try: crypt = Crypt(bs) decrypted = crypt.decrypt(x['txn']['relationship']) try: decrypted = base64.b64decode(decrypted) except: raise data = json.loads(decrypted.decode('utf-8')) x['txn']['relationship'] = data if 'postText' in data: had_txns = True self.app_log.debug('caching posts at height: {}'.format(x.get('height', 0))) for rid in rids: self.mongo.db.posts_cache.update({ 'rid': rid, 'height': x.get('height', 0), 'id': x['txn']['id'], 'bulletin_secret': bs }, { 'rid': rid, 'height': x.get('height', 0), 'id': x['txn']['id'], 'txn': x['txn'], 'bulletin_secret': bs, 'success': True }, upsert=True) except Exception as e: for rid in rids: self.mongo.db.posts_cache.update({ 'rid': rid, 'height': x.get('height', 0), 'id': x['txn']['id'], 'bulletin_secret': bs }, { 'rid': rid, 'height': x.get('height', 0), 'id': x['txn']['id'], 'txn': x['txn'], 'bulletin_secret': bs, 'success': False }, upsert=True) self.app_log.debug(e) if not had_txns: for rid in rids: self.mongo.db.posts_cache.insert({ 'rid': rid, 'height': latest_block['index'], 'success': False }) i = 1 for x in self.mongo.db.fastgraph_transaction_cache.find({ 'txn.dh_public_key': '', 'txn.relationship': {'$ne': ''}, 'txn.rid': '' }): if 'txn' in x: x['txn']['height'] = block_height + i yield x['txn'] i += 1 for x in self.mongo.db.posts_cache.find({'rid': {'$in': rids}, 'success': True}): if 'txn' in x: x['txn']['height'] = x['height'] x['txn']['bulletin_secret'] = x['bulletin_secret'] yield x['txn']
def get_transactions_by_rid(self, selector, bulletin_secret, wif=None, rid=False, raw=False, returnheight=True, lt_block_height=None, requested_rid=False): # selectors is old code before we got an RID by sorting the bulletin secrets # from block import Block # from transaction import Transaction from yadacoin.crypt import Crypt if not rid: ds = bulletin_secret selectors = [ TU.hash(ds + selector), TU.hash(selector + ds) ] else: if not isinstance(selector, list): selectors = [selector, ] else: selectors = selector transactions_by_rid_cache = self.mongo.db.transactions_by_rid_cache.find( { 'raw': raw, 'rid': rid, 'bulletin_secret': bulletin_secret, 'returnheight': returnheight, 'selector': {'$in': selectors}, 'requested_rid': requested_rid } ).sort([('height', -1)]) latest_block = self.config.BU.get_latest_block() transactions = [] if lt_block_height: query = {"transactions.rid": {"$in": selectors}, "transactions": {"$elemMatch": {"relationship": {"$ne": ""}}}, 'index': {'$lte': lt_block_height}} if requested_rid: query["transactions.requested_rid"] = {"$in": selectors} blocks = self.mongo.db.blocks.find(query) else: if transactions_by_rid_cache.count(): transactions_by_rid_cache = transactions_by_rid_cache[0] block_height = transactions_by_rid_cache['height'] else: block_height = 0 query = {"transactions.rid": {"$in": selectors}, "transactions": {"$elemMatch": {"relationship": {"$ne": ""}}}, 'index': {'$gt': block_height}} if requested_rid: query = { "$or": [ { "transactions.rid": { "$in": selectors } }, { "transactions.requested_rid": { "$in": selectors } } ], "transactions": { "$elemMatch": { "relationship": { "$ne": "" } } }, 'index': { '$gt': block_height } } else: query = { "transactions.rid": { "$in": selectors }, "transactions": { "$elemMatch": { "relationship": { "$ne": "" } } }, 'index': { '$gt': block_height } } blocks = self.mongo.db.blocks.find(query) cipher = Crypt(self.config.wif) for block in blocks: for transaction in block.get('transactions'): if 'relationship' in transaction and transaction['relationship']: if returnheight: transaction['height'] = block['index'] if not raw: try: decrypted = cipher.decrypt(transaction['relationship']) relationship = json.loads(decrypted.decode('latin1')) transaction['relationship'] = relationship except: continue for selector in selectors: self.app_log.debug('caching transactions_by_rid at height: {}'.format(block['index'])) self.mongo.db.transactions_by_rid_cache.insert( { 'raw': raw, 'rid': rid, 'bulletin_secret': bulletin_secret, 'returnheight': returnheight, 'selector': selector, 'txn': transaction, 'height': block['index'], 'requested_rid': requested_rid } ) transactions.append(transaction) if not transactions: for selector in selectors: self.mongo.db.transactions_by_rid_cache.insert( { 'raw': raw, 'rid': rid, 'bulletin_secret': bulletin_secret, 'returnheight': returnheight, 'selector': selector, 'height': latest_block['index'], 'requested_rid': requested_rid } ) for ftxn in self.mongo.db.fastgraph_transactions.find({'txn.rid': {'$in': selectors}}): if 'txn' in ftxn: yield ftxn['txn'] last_id = '' for x in self.mongo.db.transactions_by_rid_cache.find({ 'raw': raw, 'rid': rid, 'returnheight': returnheight, 'selector': {'$in': selectors}, 'requested_rid': requested_rid }).sort([('txn.id', 1)]): if 'txn' in x and x['txn']['id'] != last_id: last_id = x['txn']['id'] yield x['txn']
def get_transactions(self, wif, query, queryType, raw=False, both=True, skip=None): if not skip: skip = [] #from block import Block #from transaction import Transaction from yadacoin.crypt import Crypt get_transactions_cache = self.mongo.db.get_transactions_cache.find( { 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'queryType': queryType } ).sort([('height', -1)]) latest_block = self.get_latest_block() if get_transactions_cache.count(): get_transactions_cache = get_transactions_cache[0] block_height = get_transactions_cache['height'] else: block_height = 0 cipher = Crypt(wif) transactions = [] for block in self.mongo.db.blocks.find({"transactions": {"$elemMatch": {"relationship": {"$ne": ""}}}, 'index': {'$gt': block_height}}): for transaction in block.get('transactions'): try: if transaction.get('id') in skip: continue if 'relationship' not in transaction: continue if not transaction['relationship']: continue if not raw: decrypted = cipher.decrypt(transaction['relationship']) relationship = json.loads(decrypted.decode('latin1')) transaction['relationship'] = relationship transaction['height'] = block['index'] self.mongo.db.get_transactions_cache.update( { 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'height': latest_block['index'], 'queryType': queryType, 'id': transaction['id'] }, { 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'height': latest_block['index'], 'txn': transaction, 'queryType': queryType, 'id': transaction['id'] } , upsert=True) except: self.app_log.debug('failed decrypt. block: {}'.format(block['index'])) if both: transaction['height'] = block['index'] self.mongo.db.get_transactions_cache.update( { 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'height': latest_block['index'], 'queryType': queryType }, { 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'height': latest_block['index'], 'txn': transaction, 'queryType': queryType } , upsert=True) continue if not transactions: self.mongo.db.get_transactions_cache.insert({ 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'queryType': queryType, 'height': latest_block['index'] }) fastgraph_transactions = self.get_fastgraph_transactions(wif, query, queryType, raw=False, both=True, skip=None) for fastgraph_transaction in fastgraph_transactions: yield fastgraph_transaction search_query = { 'public_key': self.config.public_key, 'raw': raw, 'both': both, 'skip': skip, 'queryType': queryType, 'txn': {'$exists': True} } search_query.update(query) transactions = self.mongo.db.get_transactions_cache.find(search_query).sort([('height', -1)]) for transaction in transactions: yield transaction['txn']
def __init__( self, block_height, bulletin_secret='', username='', value=0, fee=0.0, requester_rid='', requested_rid='', public_key='', dh_public_key='', private_key='', dh_private_key='', to='', inputs='', outputs='', coinbase=False, chattext=None, signin=None, no_relationship=False ): self.config = get_config() self.mongo = self.config.mongo self.app_log = getLogger('tornado.application') self.block_height = block_height self.bulletin_secret = bulletin_secret self.username = username self.requester_rid = requester_rid self.requested_rid = requested_rid self.public_key = public_key self.dh_public_key = dh_public_key self.private_key = private_key self.value = value self.fee = float(fee) self.dh_private_key = dh_private_key self.to = to self.time = str(int(time.time())) self.outputs = [] self.no_relationship = no_relationship for x in outputs: self.outputs.append(Output.from_dict(x)) self.inputs = [] for x in inputs: if 'signature' in x and 'public_key' in x and 'address' in x: self.inputs.append(ExternalInput.from_dict(x)) else: self.inputs.append(Input.from_dict(x)) self.coinbase = coinbase self.chattext = chattext self.signin = signin self.do_money() inputs_concat = self.get_input_hashes() outputs_concat = self.get_output_hashes() if bulletin_secret: self.rid = self.generate_rid() if self.chattext: self.relationship = json.dumps({ "chatText": self.chattext }) self.cipher = Crypt(self.config.wif) self.encrypted_relationship = self.cipher.encrypt(self.relationship) elif self.signin: for shared_secret in TU.get_shared_secrets_by_rid(self.rid): self.relationship = SignIn(self.signin) self.cipher = Crypt(shared_secret.hex(), shared=True) self.encrypted_relationship = self.cipher.shared_encrypt(self.relationship.to_json()) break elif self.no_relationship: self.encrypted_relationship = '' else: if not self.dh_public_key or not self.dh_private_key: a = os.urandom(32).decode('latin1') self.dh_public_key = scalarmult_base(a).encode('latin1').hex() self.dh_private_key = a.encode().hex() self.relationship = self.generate_relationship() if not private_key: raise Exception('missing private key') self.cipher = Crypt(self.config.wif) self.encrypted_relationship = self.cipher.encrypt(self.relationship.to_json()) else: self.rid = '' self.encrypted_relationship = '' self.header = ( self.public_key + self.time + self.dh_public_key + self.rid + self.encrypted_relationship + "{0:.8f}".format(self.fee) + self.requester_rid + self.requested_rid + inputs_concat + outputs_concat ) self.hash = hashlib.sha256(self.header.encode('utf-8')).digest().hex() if self.private_key: self.transaction_signature = TU.generate_signature_with_private_key(private_key, self.hash) else: self.transaction_signature = '' self.transaction = self.generate_transaction()
class TransactionFactory(object): def __init__( self, block_height, bulletin_secret='', username='', value=0, fee=0.0, requester_rid='', requested_rid='', public_key='', dh_public_key='', private_key='', dh_private_key='', to='', inputs='', outputs='', coinbase=False, chattext=None, signin=None, no_relationship=False ): self.config = get_config() self.mongo = self.config.mongo self.app_log = getLogger('tornado.application') self.block_height = block_height self.bulletin_secret = bulletin_secret self.username = username self.requester_rid = requester_rid self.requested_rid = requested_rid self.public_key = public_key self.dh_public_key = dh_public_key self.private_key = private_key self.value = value self.fee = float(fee) self.dh_private_key = dh_private_key self.to = to self.time = str(int(time.time())) self.outputs = [] self.no_relationship = no_relationship for x in outputs: self.outputs.append(Output.from_dict(x)) self.inputs = [] for x in inputs: if 'signature' in x and 'public_key' in x and 'address' in x: self.inputs.append(ExternalInput.from_dict(x)) else: self.inputs.append(Input.from_dict(x)) self.coinbase = coinbase self.chattext = chattext self.signin = signin self.do_money() inputs_concat = self.get_input_hashes() outputs_concat = self.get_output_hashes() if bulletin_secret: self.rid = self.generate_rid() if self.chattext: self.relationship = json.dumps({ "chatText": self.chattext }) self.cipher = Crypt(self.config.wif) self.encrypted_relationship = self.cipher.encrypt(self.relationship) elif self.signin: for shared_secret in TU.get_shared_secrets_by_rid(self.rid): self.relationship = SignIn(self.signin) self.cipher = Crypt(shared_secret.hex(), shared=True) self.encrypted_relationship = self.cipher.shared_encrypt(self.relationship.to_json()) break elif self.no_relationship: self.encrypted_relationship = '' else: if not self.dh_public_key or not self.dh_private_key: a = os.urandom(32).decode('latin1') self.dh_public_key = scalarmult_base(a).encode('latin1').hex() self.dh_private_key = a.encode().hex() self.relationship = self.generate_relationship() if not private_key: raise Exception('missing private key') self.cipher = Crypt(self.config.wif) self.encrypted_relationship = self.cipher.encrypt(self.relationship.to_json()) else: self.rid = '' self.encrypted_relationship = '' self.header = ( self.public_key + self.time + self.dh_public_key + self.rid + self.encrypted_relationship + "{0:.8f}".format(self.fee) + self.requester_rid + self.requested_rid + inputs_concat + outputs_concat ) self.hash = hashlib.sha256(self.header.encode('utf-8')).digest().hex() if self.private_key: self.transaction_signature = TU.generate_signature_with_private_key(private_key, self.hash) else: self.transaction_signature = '' self.transaction = self.generate_transaction() def do_money(self): my_address = str(P2PKHBitcoinAddress.from_pubkey(bytes.fromhex(self.public_key))) 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']) if self.inputs: inputs = self.inputs elif self.coinbase: inputs = [] else: input_txns = self.config.BU.get_wallet_unspent_transactions(my_address) inputs = [] for input_txn in input_txns: if input_txn['id'] not in mtxn_ids: if 'signature' in input_txn and 'public_key' in input_txn and 'address' in input_txn: inputs.append(ExternalInput.from_dict(input_txn)) else: inputs.append(Input.from_dict(input_txn)) outputs_and_fee_total = sum([x.value for x in self.outputs])+self.fee input_sum = 0 if self.coinbase: self.inputs = [] else: if inputs: needed_inputs = [] done = False for y in inputs: txn = self.config.BU.get_transaction_by_id(y.id, instance=True) if not txn: raise MissingInputTransactionException() if isinstance(y, ExternalInput): y.verify() address = str(P2PKHBitcoinAddress.from_pubkey(bytes.fromhex(txn.public_key))) else: address = my_address for txn_output in txn.outputs: if txn_output.to == address: input_sum += txn_output.value needed_inputs.append(y) if input_sum >= (outputs_and_fee_total): done = True break if done: 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_input_hashes(self): from yadacoin.fastgraph import FastGraph input_hashes = [] for x in self.inputs: txn = self.config.BU.get_transaction_by_id(x.id, instance=True, include_fastgraph=isinstance(self, FastGraph)) input_hashes.append(str(txn.transaction_signature)) return ''.join(sorted(input_hashes, key=str.lower)) def get_output_hashes(self): outputs_sorted = sorted([x.to_dict() for x in self.outputs], key=lambda x: x['to'].lower()) return ''.join([x['to'] + "{0:.8f}".format(x['value']) for x in outputs_sorted]) def generate_rid(self): my_bulletin_secret = self.config.get_bulletin_secret() if my_bulletin_secret == self.bulletin_secret: raise Exception('bulletin secrets are identical. do you love yourself so much that you want a relationship on the blockchain?') bulletin_secrets = sorted([str(my_bulletin_secret), str(self.bulletin_secret)], key=str.lower) return hashlib.sha256((str(bulletin_secrets[0]) + str(bulletin_secrets[1])).encode('utf-8')).digest().hex() def generate_relationship(self): return Relationship( dh_private_key=self.dh_private_key, their_bulletin_secret=self.bulletin_secret, their_username=self.username, my_bulletin_secret=self.config.get_bulletin_secret(), my_username=self.config.username ) def generate_transaction(self): return Transaction( self.block_height, self.time, self.rid, self.transaction_signature, self.encrypted_relationship, self.public_key, self.dh_public_key, float(self.fee), self.requester_rid, self.requested_rid, self.hash, inputs=[x.to_dict() for x in self.inputs], outputs=[x.to_dict() for x in self.outputs], coinbase=self.coinbase ) def generate_transaction_signature(self): return TU.generate_signature(self.hash, self.private_key)