Esempio n. 1
0
    def tx_sources(self, amount, community):
        """
        Get inputs to generate a transaction with a given amount of money

        :param int amount: The amount target value
        :param community: The community target of the transaction

        :return: The list of inputs to use in the transaction document
        """
        amount, amount_base = reduce_base(amount, 0)
        cache = self.caches[community.currency]
        current_base = amount_base
        while current_base >= 0:
            value = 0
            sources = []
            buf_sources = list(cache.available_sources)
            for s in [src for src in cache.available_sources if src['base'] == current_base]:
                value += s['amount'] * pow(10, s['base'])
                sources.append(s)
                buf_sources.remove(s)
                if value >= amount * pow(10, amount_base):
                    overhead = value - int(amount) * pow(10, amount_base)
                    overhead, overhead_max_base = reduce_base(overhead, 0)
                    if overhead_max_base >= current_base:
                        return (sources, buf_sources)
            current_base -= 1

        raise NotEnoughMoneyError(value, community.currency,
                                  len(sources), amount * pow(10, amount_base))
Esempio n. 2
0
    def tx_sources(self, amount, community):
        """
        Get inputs to generate a transaction with a given amount of money

        :param int amount: The amount target value
        :param community: The community target of the transaction

        :return: The list of inputs to use in the transaction document
        """

        # such a dirty algorithmm
        # everything should be done again from scratch
        # in future versions

        def current_value(inputs, overhs):
            i = 0
            for s in inputs:
                i += s['amount'] * (10**s['base'])
            for o in overhs:
                i -= o[0] * (10**o[1])
            return i

        amount, amount_base = reduce_base(amount, 0)
        cache = self.caches[community.currency]
        if cache.available_sources:
            current_base = max([src['base'] for src in cache.available_sources])
            value = 0
            sources = []
            outputs = []
            overheads = []
            buf_sources = list(cache.available_sources)
            while current_base >= 0:
                for s in [src for src in cache.available_sources if src['base'] == current_base]:
                    test_sources = sources + [s]
                    val = current_value(test_sources, overheads)
                    # if we have to compute an overhead
                    if current_value(test_sources, overheads) > amount * (10**amount_base):
                        overhead = current_value(test_sources, overheads) - int(amount) * (10**amount_base)
                        # we round the overhead in the current base
                        # exemple : 12 in base 1 -> 1*10^1
                        overhead = int(round(float(overhead) / (10**current_base)))
                        source_value = s['amount'] * (10**s['base'])
                        out = int((source_value - (overhead * (10**current_base)))/(10**current_base))
                        if out * (10**current_base) <= amount * (10**amount_base):
                            sources.append(s)
                            buf_sources.remove(s)
                            overheads.append((overhead, current_base))
                            outputs.append((out, current_base))
                    # else just add the output
                    else:
                        sources.append(s)
                        buf_sources.remove(s)
                        outputs.append((s['amount'] , s['base']))
                    if current_value(sources, overheads) == amount * (10 ** amount_base):
                        return sources, outputs, overheads, buf_sources

                current_base -= 1

        raise NotEnoughMoneyError(value, community.currency,
                                  len(sources), amount * pow(10, amount_base))
Esempio n. 3
0
    def prepare_tx(self, key, receiver, blockstamp, amount, amount_base, message, currency):
        """
        Prepare a simple Transaction document
        :param SigningKey key: the issuer of the transaction
        :param str receiver: the target of the transaction
        :param duniterpy.documents.BlockUID blockstamp: the blockstamp
        :param int amount: the amount sent to the receiver
        :param int amount_base: the amount base of the currency
        :param str message: the comment of the tx
        :param str currency: the target community
        :return: the transaction document
        :rtype: List[sakia.data.entities.Transaction]
        """
        forged_tx = []
        sources = [None]*41
        while len(sources) > 40:
            result = self.tx_sources(int(amount), amount_base, currency, key.pubkey)
            sources = result[0]
            computed_outputs = result[1]
            overheads = result[2]
            # Fix issue #594
            if len(sources) > 40:
                sources_value = 0
                for s in sources[:39]:
                    sources_value += s.amount * (10**s.base)
                sources_value, sources_base = reduce_base(sources_value, 0)
                chained_tx = self.prepare_tx(key, key.pubkey, blockstamp,
                                             sources_value, sources_base, "[CHAINED]", currency)
                forged_tx += chained_tx
        self._sources_processor.consume(sources)
        logging.debug("Inputs : {0}".format(sources))

        inputs = self.tx_inputs(sources)
        unlocks = self.tx_unlocks(sources)
        outputs = self.tx_outputs(key.pubkey, receiver, computed_outputs, overheads)
        logging.debug("Outputs : {0}".format(outputs))
        txdoc = TransactionDoc(10, currency, blockstamp, 0,
                               [key.pubkey], inputs, unlocks,
                               outputs, message, None)
        txdoc.sign([key])
        self.commit_outputs_to_self(currency, key.pubkey, txdoc)
        time = self._blockchain_processor.time(currency)
        tx = Transaction(currency=currency,
                         pubkey=key.pubkey,
                         sha_hash=txdoc.sha_hash,
                         written_block=0,
                         blockstamp=blockstamp,
                         timestamp=time,
                         signatures=txdoc.signatures,
                         issuers=[key.pubkey],
                         receivers=[receiver],
                         amount=amount,
                         amount_base=amount_base,
                         comment=txdoc.comment,
                         txid=0,
                         state=Transaction.TO_SEND,
                         local=True,
                         raw=txdoc.signed_raw())
        forged_tx.append(tx)
        return forged_tx
Esempio n. 4
0
    def prepare_tx(self, key, receiver, blockstamp, amount, amount_base, message, currency):
        """
        Prepare a simple Transaction document
        :param SigningKey key: the issuer of the transaction
        :param str receiver: the target of the transaction
        :param duniterpy.documents.BlockUID blockstamp: the blockstamp
        :param int amount: the amount sent to the receiver
        :param int amount_base: the amount base of the currency
        :param str message: the comment of the tx
        :param str currency: the target community
        :return: the transaction document
        :rtype: List[sakia.data.entities.Transaction]
        """
        forged_tx = []
        sources = [None]*41
        while len(sources) > 40:
            result = self.tx_sources(int(amount), amount_base, currency, key.pubkey)
            sources = result[0]
            computed_outputs = result[1]
            overheads = result[2]
            # Fix issue #594
            if len(sources) > 40:
                sources_value = 0
                for s in sources[:39]:
                    sources_value += s.amount * (10**s.base)
                sources_value, sources_base = reduce_base(sources_value, 0)
                chained_tx = self.prepare_tx(key, key.pubkey, blockstamp,
                                             sources_value, sources_base, "[CHAINED]", currency)
                forged_tx += chained_tx
        self._sources_processor.consume(sources)
        logging.debug("Inputs : {0}".format(sources))

        inputs = self.tx_inputs(sources)
        unlocks = self.tx_unlocks(sources)
        outputs = self.tx_outputs(key.pubkey, receiver, computed_outputs, overheads)
        logging.debug("Outputs : {0}".format(outputs))
        txdoc = TransactionDoc(10, currency, blockstamp, 0,
                               [key.pubkey], inputs, unlocks,
                               outputs, message, None)
        txdoc.sign([key])
        self.commit_outputs_to_self(currency, key.pubkey, txdoc)
        time = self._blockchain_processor.time(currency)
        tx = Transaction(currency=currency,
                         pubkey=key.pubkey,
                         sha_hash=txdoc.sha_hash,
                         written_block=0,
                         blockstamp=blockstamp,
                         timestamp=time,
                         signatures=txdoc.signatures,
                         issuers=[key.pubkey],
                         receivers=[receiver],
                         amount=amount,
                         amount_base=amount_base,
                         comment=txdoc.comment,
                         txid=0,
                         state=Transaction.TO_SEND,
                         local=True,
                         raw=txdoc.signed_raw())
        forged_tx.append(tx)
        return forged_tx
Esempio n. 5
0
    def tx_sources(self, amount, amount_base, currency, pubkey):
        """
        Get inputs to generate a transaction with a given amount of money
        :param int amount: The amount target value
        :param int amount_base: The amount base target value
        :param str currency: The community target of the transaction
        :param str pubkey: The pubkey owning the sources
        :return: The list of inputs to use in the transaction document
        """

        # such a dirty algorithmm
        # everything should be done again from scratch
        # in future versions

        def current_value(inputs, overhs):
            i = 0
            for s in inputs:
                i += s.amount * (10**s.base)
            for o in overhs:
                i -= o[0] * (10**o[1])
            return i

        amount, amount_base = reduce_base(amount, amount_base)
        available_sources = self._sources_processor.available(currency, pubkey)
        if available_sources:
            current_base = max([src.base for src in available_sources])
            value = 0
            sources = []
            outputs = []
            overheads = []
            buf_sources = list(available_sources)
            while current_base >= 0:
                for s in [src for src in available_sources if src.base == current_base]:
                    test_sources = sources + [s]
                    val = current_value(test_sources, overheads)
                    # if we have to compute an overhead
                    if current_value(test_sources, overheads) > amount * (10**amount_base):
                        overhead = current_value(test_sources, overheads) - int(amount) * (10**amount_base)
                        # we round the overhead in the current base
                        # exemple : 12 in base 1 -> 1*10^1
                        overhead = int(round(float(overhead) / (10**current_base)))
                        source_value = s.amount * (10**s.base)
                        out = int((source_value - (overhead * (10**current_base)))/(10**current_base))
                        if out * (10**current_base) <= amount * (10**amount_base):
                            sources.append(s)
                            buf_sources.remove(s)
                            overheads.append((overhead, current_base))
                            outputs.append((out, current_base))
                    # else just add the output
                    else:
                        sources.append(s)
                        buf_sources.remove(s)
                        outputs.append((s.amount, s.base))
                    if current_value(sources, overheads) == amount * (10 ** amount_base):
                        return sources, outputs, overheads

                current_base -= 1

        raise NotEnoughChangeError(value, currency, len(sources), amount * pow(10, amount_base))
Esempio n. 6
0
    def outputs_from_sources(self, amount, sources):
        # such a dirty algorithmm
        # shamelessly copy pasted from sakia
        def current_value(inputs, overhs):
            i = 0
            for s in inputs:
                i += s.amount * (10**s.base)
            for o in overhs:
                i -= o[0] * (10**o[1])
            return i

        amount, amount_base = reduce_base(amount, 0)
        current_base = max([src.base for src in sources])
        result_sources = []
        outputs = []
        overheads = []
        buf_sources = list(sources)
        while current_base >= 0:
            for s in [src for src in buf_sources if src.base == current_base]:
                test_sources = result_sources + [s]
                val = current_value(test_sources, overheads)
                # if we have to compute an overhead
                if current_value(test_sources,
                                 overheads) > amount * (10**amount_base):
                    overhead = current_value(
                        test_sources,
                        overheads) - int(amount) * (10**amount_base)
                    # we round the overhead in the current base
                    # exemple : 12 in base 1 -> 1*10^1
                    overhead = int(round(float(overhead) / (10**current_base)))
                    source_value = s.amount * (10**s.base)
                    out = int(
                        (source_value -
                         (overhead * (10**current_base))) / (10**current_base))
                    if out * (10**current_base) <= amount * (10**amount_base):
                        result_sources.append(s)
                        buf_sources.remove(s)
                        overheads.append((overhead, current_base))
                        outputs.append((out, current_base))
                # else just add the output
                else:
                    result_sources.append(s)
                    buf_sources.remove(s)
                    outputs.append((s.amount, s.base))
                if current_value(result_sources,
                                 overheads) == amount * (10**amount_base):
                    return result_sources, outputs, overheads

            current_base -= 1
        raise ValueError("Not enough sources")
Esempio n. 7
0
def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
    """
    Parse a transaction
    :param duniterpy.documents.Transaction tx_doc: The tx json data
    :param str pubkey: The pubkey of the transaction to parse, to know if its a receiver or issuer
    :param int block_number: The block number where we found the tx
    :param int mediantime: Median time on the network
    :param int txid: The latest txid
    :return: the found transaction
    """
    receivers = [
        o.conditions.left.pubkey for o in tx_doc.outputs
        if o.conditions.left.pubkey != tx_doc.issuers[0]
    ]

    in_issuers = len([i for i in tx_doc.issuers if i == pubkey]) > 0
    in_outputs = len(
        [o for o in tx_doc.outputs if o.conditions.left.pubkey == pubkey]) > 0

    if len(receivers) == 0:
        receivers = [tx_doc.issuers[0]]
        # Transaction to self
        outputs = [o for o in tx_doc.outputs]
        amount = 0
        for o in outputs:
            amount += o.amount * math.pow(10, o.base)
        amount, amount_base = reduce_base(amount, 0)
    elif in_issuers or in_outputs:
        # If the wallet pubkey is in the issuers we sent this transaction
        if in_issuers:
            outputs = [
                o for o in tx_doc.outputs if o.conditions.left.pubkey != pubkey
            ]
            amount = 0
            for o in outputs:
                amount += o.amount * math.pow(10, o.base)
        # If we are not in the issuers,
        # maybe we are in the recipients of this transaction
        else:
            outputs = [
                o for o in tx_doc.outputs if o.conditions.left.pubkey == pubkey
            ]
        amount = 0
        for o in outputs:
            amount += o.amount * math.pow(10, o.base)
        amount, amount_base = reduce_base(amount, 0)
    else:
        return None

    transaction = Transaction(currency=tx_doc.currency,
                              pubkey=pubkey,
                              sha_hash=tx_doc.sha_hash,
                              written_block=block_number,
                              blockstamp=tx_doc.blockstamp,
                              timestamp=mediantime,
                              signatures=tx_doc.signatures,
                              issuers=tx_doc.issuers,
                              receivers=receivers,
                              amount=amount,
                              amount_base=amount_base,
                              comment=tx_doc.comment,
                              txid=txid,
                              state=Transaction.VALIDATED,
                              raw=tx_doc.signed_raw())
    return transaction
 def test_reduce_base_2(self):
     amount = 120
     base = 4
     computed = reduce_base(amount, base)
     self.assertEqual(computed[0], 12)
     self.assertEqual(computed[1], 5)
Esempio n. 9
0
def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
    """
    Parse a transaction
    :param duniterpy.documents.Transaction tx_doc: The tx json data
    :param str pubkey: The pubkey of the transaction to parse, to know if its a receiver or issuer
    :param int block_number: The block number where we found the tx
    :param int mediantime: Median time on the network
    :param int txid: The latest txid
    :return: the found transaction
    """
    receivers = [o.conditions.left.pubkey for o in tx_doc.outputs
                 if o.conditions.left.pubkey != tx_doc.issuers[0]]

    in_issuers = len([i for i in tx_doc.issuers
                      if i == pubkey]) > 0

    in_outputs = len([o for o in tx_doc.outputs
                      if o.conditions.left.pubkey == pubkey]) > 0

    if len(receivers) == 0 and in_issuers:
        receivers = [tx_doc.issuers[0]]
        # Transaction to self
        outputs = [o for o in tx_doc.outputs]
        amount = 0
        for o in outputs:
            amount += o.amount * math.pow(10, o.base)
        amount, amount_base = reduce_base(amount, 0)
    elif in_issuers or in_outputs:
        # If the wallet pubkey is in the issuers we sent this transaction
        if in_issuers:
            outputs = [o for o in tx_doc.outputs
                       if o.conditions.left.pubkey != pubkey]
            amount = 0
            for o in outputs:
                amount += o.amount * math.pow(10, o.base)
        # If we are not in the issuers,
        # maybe we are in the recipients of this transaction
        else:
            outputs = [o for o in tx_doc.outputs
                       if o.conditions.left.pubkey == pubkey]
        amount = 0
        for o in outputs:
            amount += o.amount * math.pow(10, o.base)
        amount, amount_base = reduce_base(amount, 0)
    else:
        return None

    transaction = Transaction(currency=tx_doc.currency,
                              pubkey=pubkey,
                              sha_hash=tx_doc.sha_hash,
                              written_block=block_number,
                              blockstamp=tx_doc.blockstamp,
                              timestamp=mediantime,
                              signatures=tx_doc.signatures,
                              issuers=tx_doc.issuers,
                              receivers=receivers,
                              amount=amount,
                              amount_base=amount_base,
                              comment=tx_doc.comment,
                              txid=txid,
                              state=Transaction.VALIDATED,
                              raw=tx_doc.signed_raw())
    return transaction