Esempio n. 1
0
class BotTransactionBaseClass(TransactionBaseClass, SignatureCallbackBase):
    BOT_FEE_NETWORK_ADDRESS_UPDATE = Currency(value='20 TFT')
    BOT_FEE_ADDITIONAL_NAME = Currency(value='50 TFT')
    BOT_FEE_REGISTRATION = Currency(value='90 TFT')
    BOT_FEE_MONTHLY = Currency(value='10 TFT')

    MAX_NAMES_PER_BOT = 5
    MAX_ADDRESSES_PER_BOT = 10

    SPECIFIER_SENDER = BinaryData(value=b'sender', fixed_size=0)
    SPECIFIER_RECEIVER = BinaryData(value=b'receiver', fixed_size=0)

    @staticmethod
    def compute_monthly_bot_fees(months):
        """
        computes the total monthly fees required for the given months,
        using the given oneCoin value as the currency's unit value.
        """
        fees = BotTransactionBaseClass.BOT_FEE_MONTHLY * months
        if months < 12:
            return fees
        if months < 24:
            return fees * 0.7
        return fees * 0.5

    @property
    @abstractmethod
    def required_bot_fees(self):
        """
        The bot fees required to pay for this Bot Transaction.
        """
        pass
Esempio n. 2
0
 def unconfirmed_locked(self):
     """
     Total unconfirmed coins that are locked, and thus not available for spending.
     """
     if self.chain_time > 0 and self.chain_height > 0:
         return Currency.sum(*[
             co.value for co in jsobj.dict_values(self._outputs_unconfirmed)
             if co.condition.lock.locked_check(time=self.chain_time,
                                               height=self.chain_height)
         ]) or Currency()
     else:
         return Currency(
         )  # impossible to know for sure without a complete context
Esempio n. 3
0
    def fund(self, amount, source=None):
        """
        Fund the specified amount with the available outputs of this wallet's balance.
        """
        # collect addresses and multisig addresses
        addresses = set()
        refund = None
        if source == None:
            for co in self.outputs_available:
                addresses.add(co.condition.unlockhash.__str__())
            for co in self.outputs_unconfirmed_available:
                addresses.add(co.condition.unlockhash.__str__())
        else:
            # if only one address is given, transform it into an acceptable list
            if not isinstance(source, list) and not jsobj.is_js_arr(source):
                if isinstance(source, str):
                    source = UnlockHash.from_json(source)
                elif not isinstance(source, UnlockHash):
                    raise TypeError(
                        "cannot add source address from type {}".format(
                            type(source)))
                source = [source]
            # add one or multiple personal/multisig addresses
            for value in source:
                if isinstance(value, str):
                    value = UnlockHash.from_json(value)
                elif not isinstance(value, UnlockHash):
                    raise TypeError(
                        "cannot add source address from type {}".format(
                            type(value)))
                elif value.uhtype.__eq__(UnlockHashType.PUBLIC_KEY):
                    addresses.add(value)
                else:
                    raise TypeError(
                        "cannot add source address with unsupported UnlockHashType {}"
                        .format(value.uhtype))
            if len(source) == 1:
                if source[0].uhtype.__eq__(UnlockHashType.PUBLIC_KEY):
                    refund = ConditionTypes.unlockhash_new(
                        unlockhash=source[0])

        # ensure at least one address is defined
        if len(addresses) == 0:
            raise tferrors.InsufficientFunds(
                "insufficient funds in this wallet")

        # if personal addresses are given, try to use these first
        # as these are the easiest kind to deal with
        if len(addresses) == 0:
            outputs, collected = ([], Currency())  # start with nothing
        else:
            outputs, collected = self._fund_individual(amount, addresses)

        if collected.greater_than_or_equal_to(amount):
            # if we already have sufficient, we stop now
            return ([CoinInput.from_coin_output(co)
                     for co in outputs], collected.minus(amount), refund)
        raise tferrors.InsufficientFunds(
            "not enough funds available in the wallet to fund the requested amount"
        )
Esempio n. 4
0
 def registration_fee(self, txfee):
     if txfee is not None:
         fee = Currency(value=txfee)
         if fee != self._registration_fee:
             raise ValueError(
                 "registration fee is hardcoded at {}, cannot be set to {}".
                 format(fee.str(with_unit=True),
                        self._registration_fee.str(with_unit=True)))
Esempio n. 5
0
    def __init__(self):
        self._public_key = None
        self._signature = None
        self._registration_fee = Currency(
            value=TransactionV210.HARDCODED_REGISTRATION_FEE)
        self._transaction_fee = None
        self._coin_inputs = None
        self._refund_coin_output = None

        super().__init__()
Esempio n. 6
0
    def _fund_individual(self, amount, addresses):
        outputs_available = [
            co for co in self.outputs_available
            if co.condition.unlockhash.__str__() in addresses
        ]

        def sort_output_by_value(a, b):
            if a.value.less_than(b.value):
                return -1
            if a.value.greater_than(b.value):
                return 1
            return 0

        outputs_available = jsarr.sort(outputs_available, sort_output_by_value)

        collected = Currency()
        outputs = []
        # try to fund only with confirmed outputs, if possible
        for co in outputs_available:
            if co.value.greater_than_or_equal_to(amount):
                return [co], co.value
            collected = collected.plus(co.value)
            outputs.append(co)
            if len(outputs) > _MAX_RIVINE_TRANSACTION_INPUTS:
                # to not reach the input limit
                collected = collected.minus(jsarr.pop(outputs, 0).value)
            if collected.greater_than_or_equal_to(amount):
                return outputs, collected

        if collected.greater_than_or_equal_to(amount):
            # if we already have sufficient, we stop now
            return outputs, collected

        # use unconfirmed balance, not ideal, but acceptable
        outputs_available = [
            co for co in self.outputs_unconfirmed_available
            if co.condition.unlockhash.__str__() in addresses
        ]
        outputs_available = jsarr.sort(outputs_available,
                                       sort_output_by_value,
                                       reverse=True)
        for co in outputs_available:
            if co.value.greater_than_or_equal_to(amount):
                return [co], co.value
            collected = collected.plus(co.value)
            outputs.append(co)
            if len(outputs) > _MAX_RIVINE_TRANSACTION_INPUTS:
                # to not reach the input limit
                collected = collected.minus(outputs.pop(0).value)
            if collected.greater_than_or_equal_to(amount):
                return outputs, collected

        # we return whatever we have collected, no matter if it is sufficient
        return outputs, collected
Esempio n. 7
0
 def unconfirmed(self):
     """
     Total unconfirmed coins, available for spending.
     """
     if self.chain_time > 0 and self.chain_height > 0:
         return Currency.sum(*[
             co.value for co in jsobj.dict_values(self._outputs_unconfirmed)
             if not co.condition.lock.locked_check(time=self.chain_time,
                                                   height=self.chain_height)
         ]) or Currency()
     else:
         return Currency.sum(*[
             co.value for co in jsobj.dict_values(self._outputs_unconfirmed)
         ])
Esempio n. 8
0
 def required_bot_fees(self):
     """
     The fees required to pay for this 3Bot Record Update Transaction.
     """
     fees = Currency(value=0)
     # all months have to be paid
     if self._number_of_months > 0:
         fees += BotTransactionBaseClass.compute_monthly_bot_fees(self._number_of_months)
     # if addresses have been modified, this has to be paid
     if len(self._addresses_to_add) > 0 or len(self._addresses_to_remove) > 0:
         fees += BotTransactionBaseClass.BOT_FEE_NETWORK_ADDRESS_UPDATE
     # each additional that is added, has to be paid as well
     lnames = len(self._names_to_add)
     if lnames > 0:
         fees += BotTransactionBaseClass.BOT_FEE_ADDITIONAL_NAME * lnames
     # return the total fees
     return fees
Esempio n. 9
0
 def transaction_fee(self):
     if self._transaction_fee is None:
         return Currency()
     return self._transaction_fee
Esempio n. 10
0
def test_types():
    # currency values can be created from both
    # int and str values, but are never allowed to be negative
    jsass.equals(Currency().str(), '0')
    jsass.equals(Currency(value=123).str(), '123')
    jsass.equals(Currency(value='1').str(), '1')
    jsass.equals(Currency(value='0.1').json(), '100000000')
    # in the string versions you can also add the TFT currency notation,
    # or use decimal notation to express the currency in the TFT Currency Unit,
    # rather than the primitive unit
    jsass.equals(Currency(value='1 TFT').str(), '1')
    jsass.equals(Currency(value='0.123456789').str(), '0.123456789')
    jsass.equals(Currency(value='0.123456789').json(), '123456789')
    jsass.equals(Currency(value='9.123456789').str(), '9.123456789')
    jsass.equals(Currency(value='1234.34').str(), '1234.34')
    jsass.equals(Currency(value='1.00000').str(), '1')
    jsass.equals(Currency(value='1.0 tft').str(), '1')
    jsass.equals(Currency(value=1).str(), '1')
    jsass.equals(Currency(value=12344).str(), '12344')

    # hash values can be created directly from binary data,
    # or from a hex-encoded string, by default the nil hash will be created
    jsass.equals(Hash().str(), '0' * 64)
    jsass.equals(
        Hash(b'12345678901234567890123456789001').value,
        b'12345678901234567890123456789001')

    # binary data is very similar to a hash,
    # except that it doesn't have a fixed length and it is binary serialized
    # as a slice, not an array
    jsass.equals(BinaryData().str(), '')
    jsass.equals(BinaryData(b'1').str(), '31')
    jsass.equals(BinaryData(b'1', fixed_size=0).str(), '31')
    jsass.equals(BinaryData(b'1', fixed_size=1).str(), '31')

    # raw data is pretty much binary data, except that it is
    # base64 encoded/decoded for str/json purposes
    jsass.equals(BinaryData(b'data', strencoding='base64').str(), 'ZGF0YQ==')

    # block stake values can be created from both
    # int and str values, but are never allowed to be negative
    jsass.equals(Blockstake().str(), '0')
    jsass.equals(Blockstake(value=123).str(), '123')
    jsass.equals(Blockstake(value='1').str(), '1')
Esempio n. 11
0
 def miner_fee_add(self, value):
     self._miner_fees.append(Currency(value=value))
Esempio n. 12
0
 def value(self, value):
     if value is None:
         self._value = None
     else:
         self._value = Currency(value=value)
Esempio n. 13
0
 def value(self):
     if self._value is None:
         return Currency()
     return self._value
Esempio n. 14
0
 def minimum_miner_fee(self):
     if self == NetworkType.DEVNET:
         return Currency('1.0')
     return Currency('0.1')
Esempio n. 15
0
 def transaction_fee(self, txfee):
     if txfee is None:
         self._transaction_fee = None
     else:
         self._transaction_fee = Currency(value=txfee)
Esempio n. 16
0
 def value(self, value):
     if isinstance(value, Currency):
         self._value = value
         return
     self._value = Currency(value=value)
Esempio n. 17
0
 def minimum_miner_fee(self):
     if self.__eq__(Type.DEVNET):
         return Currency('1.0')
     return Currency('0.1')