Beispiel #1
0
 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']
Beispiel #2
0
 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
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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']
Beispiel #6
0
    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']
Beispiel #7
0
    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']
Beispiel #8
0
 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()
Beispiel #9
0
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)