Exemplo 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
Exemplo n.º 2
0
    def __init__(self):
        self._mint_fulfillment = None
        self._mint_condition = None
        self._miner_fees = []
        self._data = None
        self._nonce = BinaryData(crandom(8), strencoding='base64')

        # current mint condition
        self._parent_mint_condition = None

        super().__init__()
Exemplo n.º 3
0
    def __init__(self):
        self._mint_fulfillment = None
        self._coin_outputs = []
        self._miner_fees = []
        self._data = None
        self._nonce = BinaryData(generateXByteID(8), strencoding='base64')

        # current mint condition
        self._parent_mint_condition = None

        super().__init__()
Exemplo n.º 4
0
 def _from_json_data_object(self, data):
     self._nonce = BinaryData.from_json(data.get('nonce', ''),
                                        strencoding='base64')
     self._mint_condition = conditions.from_json(
         data.get('mintcondition', {}))
     self._mint_fulfillment = fulfillments.from_json(
         data.get('mintfulfillment', {}))
     self._miner_fees = [
         Currency.from_json(fee) for fee in data.get('minerfees', []) or []
     ]
     self._data = BinaryData.from_json(data.get('arbitrarydata', None)
                                       or '',
                                       strencoding='base64')
Exemplo n.º 5
0
 def _from_json_data_object(self, data):
     self._nonce = BinaryData.from_json(data.get('nonce', ''),
                                        strencoding='base64')
     self._mint_fulfillment = FulfillmentTypes.from_json(
         data.get('mintfulfillment', {}))
     self._coin_outputs = [
         CoinOutput.from_json(co)
         for co in data.get('coinoutputs', []) or []
     ]
     self._miner_fees = [
         Currency.from_json(fee) for fee in data.get('minerfees', []) or []
     ]
     self._data = BinaryData.from_json(data.get('arbitrarydata', None)
                                       or '',
                                       strencoding='base64')
Exemplo n.º 6
0
 def data(self):
     """
     Optional binary data attached to this Transaction,
     with a max length of 83 bytes.
     """
     if self._data is None:
         return BinaryData(strencoding='base64')
     return self._data
Exemplo n.º 7
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')
Exemplo n.º 8
0
 def data(self, value):
     if value is None:
         self._data = None
         return
     if isinstance(value, BinaryData):
         value = value.value
     elif isinstance(value, str):
         value = value.encode('utf-8')
     if len(value) > 83:
         raise ValueError(
             "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit".format(len(value)))
     self._data = BinaryData(value=value, strencoding='base64')
Exemplo n.º 9
0
 def _custom_data_setter(self, value):
     if value == None:
         self._data = None
         return
     if isinstance(value, BinaryData):
         value = value.value
     elif isinstance(value, str):
         value = jsstr.to_utf8(value)
     if len(value) > 83:
         raise ValueError(
             "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit".format(len(value)))
     self._data = BinaryData(value=value, strencoding='base64')
Exemplo n.º 10
0
    def __init__(self):
        self._coin_inputs = []
        self._coin_outputs = []
        self._blockstake_inputs = []
        self._blockstake_outputs = []
        self._miner_fees = []
        self._data = BinaryData(strencoding='base64')

        # hidden flag, that indicates if this Txn was a Legacy v0 Txn or not,
        # False by default as we do not wish to produce new legacy Txns, only decode existing ones
        self._legacy = False

        super().__init__()
Exemplo n.º 11
0
 def _from_json_data_object(self, data):
     self._coin_inputs = [CoinInput.from_json(
         ci) for ci in data.get('coininputs', []) or []]
     self._coin_outputs = [CoinOutput.from_json(
         co) for co in data.get('coinoutputs', []) or []]
     self._blockstake_inputs = [BlockstakeInput.from_json(
         bsi) for bsi in data.get('blockstakeinputs', []) or []]
     self._blockstake_outputs = [BlockstakeOutput.from_json(
         bso) for bso in data.get('blockstakeoutputs', []) or []]
     self._miner_fees = [Currency.from_json(
         fee) for fee in data.get('minerfees', []) or []]
     self._data = BinaryData.from_json(
         data.get('arbitrarydata', None) or '', strencoding='base64')
 def hashed_secret(self):
     if self._hashed_secret == None:
         return BinaryData()
     return self._hashed_secret
Exemplo n.º 13
0
class TransactionV129(TransactionBaseClass):
    _SPECIFIER = b'coin mint tx\0\0\0\0'

    def __init__(self):
        self._mint_fulfillment = None
        self._coin_outputs = []
        self._miner_fees = []
        self._data = None
        self._nonce = BinaryData(generateXByteID(8), strencoding='base64')

        # current mint condition
        self._parent_mint_condition = None

        super().__init__()

    @property
    def version(self):
        return TransactionVersion.MINTER_COIN_CREATION

    @property
    def miner_fees(self):
        """
        Miner fees, paid to the block creator of this Transaction,
        funded by this Transaction's coin inputs.
        """
        return self._miner_fees

    @property
    def data(self):
        """
        Optional binary data attached to this Transaction,
        with a max length of 83 bytes.
        """
        if self._data is None:
            return BinaryData(strencoding='base64')
        return self._data

    @data.setter
    def data(self, value):
        if value is None:
            self._data = None
            return
        if isinstance(value, BinaryData):
            value = value.value
        elif isinstance(value, str):
            value = value.encode('utf-8')
        if len(value) > 83:
            raise ValueError(
                "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit"
                .format(len(value)))
        self._data = BinaryData(value=value, strencoding='base64')

    @property
    def coin_outputs(self):
        """
        Coin outputs of this Transaction,
        funded by the Transaction's coin inputs.
        """
        return self._coin_outputs

    @coin_outputs.setter
    def coin_outputs(self, value):
        self._coin_outputs = []
        if not value:
            return
        for co in value:
            self.coin_output_add(co.value, co.condition, id=co.id)

    def coin_output_add(self, value, condition, id=None):
        co = CoinOutput(value=value, condition=condition)
        co.id = id
        self._coin_outputs.append(co)

    def miner_fee_add(self, value):
        self._miner_fees.append(Currency(value=value))

    def mint_fulfillment_defined(self):
        return self._mint_fulfillment is not None

    @property
    def mint_fulfillment(self):
        """
        Retrieve the current mint fulfillment
        """
        if self._mint_fulfillment is None:
            return FulfillmentSingleSignature()
        return self._mint_fulfillment

    @mint_fulfillment.setter
    def mint_fulfillment(self, value):
        if value is None:
            self._mint_fulfillment = None
            return
        if not isinstance(value, FulfillmentBaseClass):
            raise TypeError(
                "CoinCreation (v129) Transaction's mint fulfillment has to be a subtype of FulfillmentBaseClass, not {}"
                .format(type(value)))
        self._mint_fulfillment = value

    @property
    def parent_mint_condition(self):
        """
        Retrieve the parent mint condition which will be set
        """
        if self._parent_mint_condition is None:
            return ConditionNil()
        return self._parent_mint_condition

    @parent_mint_condition.setter
    def parent_mint_condition(self, value):
        if value is None:
            self._parent_mint_condition = None
            return
        if not isinstance(value, ConditionBaseClass):
            raise TypeError(
                "CoinCreation (v129) Transaction's parent mint condition has to be a subtype of ConditionBaseClass, not {}"
                .format(type(value)))
        self._parent_mint_condition = value

    def _signature_hash_input_get(self, *extra_objects):
        e = encoder_sia_get()

        # encode the transaction version
        e.add_byte(self.version)

        # encode the specifier
        e.add_array(TransactionV129._SPECIFIER)

        # encode nonce
        e.add_array(self._nonce.value)

        # extra objects if any
        if extra_objects:
            e.add_all(*extra_objects)

        # encode coin outputs
        e.add_slice(self.coin_outputs)

        # encode miner fees
        e.add_slice(self.miner_fees)

        # encode custom data
        e.add(self.data)

        # return the encoded data
        return e.data

    def _id_input_compute(self):
        return bytearray(
            TransactionV129._SPECIFIER) + self._binary_encode_data()

    def _binary_encode_data(self):
        encoder = encoder_sia_get()
        encoder.add_array(self._nonce.value)
        encoder.add_all(
            self.mint_fulfillment,
            self.coin_outputs,
            self.miner_fees,
            self.data,
        )
        return encoder.data

    def _from_json_data_object(self, data):
        self._nonce = BinaryData.from_json(data.get('nonce', ''),
                                           strencoding='base64')
        self._mint_fulfillment = fulfillments.from_json(
            data.get('mintfulfillment', {}))
        self._coin_outputs = [
            CoinOutput.from_json(co)
            for co in data.get('coinoutputs', []) or []
        ]
        self._miner_fees = [
            Currency.from_json(fee) for fee in data.get('minerfees', []) or []
        ]
        self._data = BinaryData.from_json(data.get('arbitrarydata', None)
                                          or '',
                                          strencoding='base64')

    def _json_data_object(self):
        return {
            'nonce': self._nonce.json(),
            'mintfulfillment': self.mint_fulfillment.json(),
            'coinoutputs': [co.json() for co in self.coin_outputs],
            'minerfees': [fee.json() for fee in self.miner_fees],
            'arbitrarydata': self.data.json(),
        }

    def _extra_signature_requests_new(self):
        if self._parent_mint_condition is None:
            return []  # nothing to be signed
        return self._mint_fulfillment.signature_requests_new(
            # no extra objects are to be included within txn scope
            input_hash_func=self.signature_hash_get,
            parent_condition=self._parent_mint_condition,
        )

    def _extra_is_fulfilled(self):
        if self._parent_mint_condition is None:
            return False
        return self.mint_fulfillment.is_fulfilled(
            parent_condition=self._parent_mint_condition)
Exemplo n.º 14
0
class TransactionV128(TransactionBaseClass):
    _SPECIFIER = b'minter defin tx\0'

    def __init__(self):
        self._mint_fulfillment = None
        self._mint_condition = None
        self._miner_fees = []
        self._data = None
        self._nonce = BinaryData(crandom(8), strencoding='base64')

        # current mint condition
        self._parent_mint_condition = None

        super().__init__()

    def _custom_version_getter(self):
        return TransactionVersion.MINTER_DEFINITION

    def _custom_miner_fees_getter(self):
        """
        Miner fees, paid to the block creator of this Transaction,
        funded by this Transaction's coin inputs.
        """
        return self._miner_fees

    def _custom_data_getter(self):
        """
        Optional binary data attached to this Transaction,
        with a max length of 83 bytes.
        """
        if self._data == None:
            return BinaryData(strencoding='base64')
        return self._data

    def _custom_data_setter(self, value):
        if value == None:
            self._data = None
            return
        if isinstance(value, BinaryData):
            value = value.value
        elif isinstance(value, str):
            value = jsstr.to_utf8(value)
        if len(value) > 83:
            raise ValueError(
                "arbitrary data can have a maximum bytes length of 83, {} exceeds this limit"
                .format(len(value)))
        self._data = BinaryData(value=value, strencoding='base64')

    @property
    def mint_condition(self):
        """
        Retrieve the new mint condition which will be set
        """
        if self._mint_condition == None:
            return ConditionNil()
        return self._mint_condition

    @mint_condition.setter
    def mint_condition(self, value):
        if value == None:
            self._mint_condition = None
            return
        if not isinstance(value, ConditionBaseClass):
            raise TypeError(
                "MintDefinition (v128) Transaction's mint condition has to be a subtype of ConditionBaseClass, not {}"
                .format(type(value)))
        self._mint_condition = value

    @property
    def parent_mint_condition(self):
        """
        Retrieve the parent mint condition which will be set
        """
        if self._parent_mint_condition == None:
            return ConditionNil()
        return self._parent_mint_condition

    @parent_mint_condition.setter
    def parent_mint_condition(self, value):
        if value == None:
            self._parent_mint_condition = None
            return
        if not isinstance(value, ConditionBaseClass):
            raise TypeError(
                "MintDefinition (v128) Transaction's parent mint condition has to be a subtype of ConditionBaseClass, not {}"
                .format(type(value)))
        self._parent_mint_condition = value

    def mint_fulfillment_defined(self):
        return self._mint_fulfillment != None

    @property
    def mint_fulfillment(self):
        """
        Retrieve the current mint fulfillment
        """
        if self._mint_fulfillment == None:
            return FulfillmentSingleSignature()
        return self._mint_fulfillment

    @mint_fulfillment.setter
    def mint_fulfillment(self, value):
        if value == None:
            self._mint_fulfillment = None
            return
        if not isinstance(value, FulfillmentBaseClass):
            raise TypeError(
                "MintDefinition (v128) Transaction's mint fulfillment has to be a subtype of FulfillmentBaseClass, not {}"
                .format(type(value)))
        self._mint_fulfillment = value

    def miner_fee_add(self, value):
        self._miner_fees.append(Currency(value=value))

    def _signature_hash_input_get(self, *extra_objects):
        e = SiaBinaryEncoder()

        # encode the transaction version
        e.add_byte(self.version.value)

        # encode the specifier
        e.add_array(TransactionV128._SPECIFIER)

        # encode nonce
        e.add_array(self._nonce.value)

        # extra objects if any
        if extra_objects:
            e.add_all(*extra_objects)

        # encode new mint condition
        e.add(self.mint_condition)

        # encode miner fees
        e.add_slice(self.miner_fees)

        # encode custom data
        e.add(self.data)

        # return the encoded data
        return e.data

    def _id_input_compute(self):
        return jsarr.concat(TransactionV128._SPECIFIER,
                            self._binary_encode_data())

    def _binary_encode_data(self):
        encoder = SiaBinaryEncoder()
        encoder.add_array(self._nonce.value)
        encoder.add_all(
            self.mint_fulfillment,
            self.mint_condition,
            self.miner_fees,
            self.data,
        )

        return encoder.data

    def _from_json_data_object(self, data):
        self._nonce = BinaryData.from_json(data.get_or('nonce', ''),
                                           strencoding='base64')
        self._mint_condition = ConditionTypes.from_json(
            data.get_or('mintcondition', jsobj.new_dict()))
        self._mint_fulfillment = FulfillmentTypes.from_json(
            data.get_or('mintfulfillment', jsobj.new_dict()))
        self._miner_fees = [
            Currency.from_json(fee)
            for fee in data.get_or('minerfees', []) or []
        ]
        self._data = BinaryData.from_json(data.get_or('arbitrarydata', None)
                                          or '',
                                          strencoding='base64')

    def _json_data_object(self):
        return {
            'nonce': self._nonce.json(),
            'mintfulfillment': self.mint_fulfillment.json(),
            'mintcondition': self.mint_condition.json(),
            'minerfees': [fee.json() for fee in self._miner_fees],
            'arbitrarydata': self.data.json(),
        }

    def _extra_signature_requests_new(self):
        if self._parent_mint_condition == None:
            return []  # nothing to be signed
        return self._mint_fulfillment.signature_requests_new(
            # no extra objects are to be included within txn scope
            input_hash_func=self.signature_hash_get,
            parent_condition=self._parent_mint_condition,
        )

    def _extra_is_fulfilled(self):
        if self._parent_mint_condition == None:
            return False
        return self.mint_fulfillment.is_fulfilled(
            parent_condition=self._parent_mint_condition)
Exemplo n.º 15
0
class TransactionV210(TransactionBaseClass, SignatureCallbackBase):
    _SPECIFIER = b'erc20 addrreg tx'

    HARDCODED_REGISTRATION_FEE = '10 TFT'
    SPECIFIER_REGISTRATION_SIGNATURE = BinaryData(value=b'registration',
                                                  fixed_size=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__()

    @property
    def version(self):
        return TransactionVersion.ERC20_ADDRESS_REGISTRATION

    @property
    def public_key(self):
        if self._public_key is None:
            return PublicKey()
        return self._public_key

    @public_key.setter
    def public_key(self, value):
        if value is None:
            self._public_key = None
            return
        if not isinstance(value, PublicKey):
            raise TypeError(
                "cannot assign value of type {} as BotRegistration's public key (expected type: PublicKey)"
                .format(type(value)))
        self._public_key = PublicKey(specifier=value.specifier,
                                     hash=value.hash)

    @property
    def signature(self):
        if self._signature is None:
            return ED25519Signature(as_array=True)
        return self._signature

    @signature.setter
    def signature(self, value):
        if value is None:
            self._signature = None
            return
        self._signature = ED25519Signature(value=value, as_array=True)

    @property
    def coin_inputs(self):
        """
        Coin inputs of this Transaction,
        used as funding for coin outputs, fees and any other kind of coin output.
        """
        return self._coin_inputs

    @coin_inputs.setter
    def coin_inputs(self, value):
        self._coin_inputs = []
        if not value:
            return
        for ci in value:
            self.coin_input_add(ci.parentid,
                                ci.fulfillment,
                                parent_output=ci.parent_output)

    @property
    def refund_coin_output(self):
        if self._refund_coin_output is None:
            return CoinOutput()
        return self._refund_coin_output

    @property
    def coin_outputs(self):
        """
        Empty list, or a singleton with the refund coin output if that one exists.
        """
        if self._refund_coin_output is None:
            return []
        return [self._refund_coin_output]

    @coin_outputs.setter
    def coin_outputs(self, value):
        if isinstance(value, list):
            lvalue = len(value)
            if lvalue == 0:
                value = None
            elif lvalue == 1:
                value = value[0]
            else:
                raise ValueError(
                    "ThreeBot only can have one coin output, a refund coin output"
                )
        if value is None:
            self._refund_coin_output = None
        elif isinstance(value, CoinOutput):
            self._refund_coin_output = CoinOutput(value=value.value,
                                                  condition=value.condition)
            self._refund_coin_output.id = value.id
        else:
            raise TypeError(
                "cannot assign a value of type {} to coin outputs".format(
                    type(value)))

    def coin_input_add(self, parentid, fulfillment, parent_output=None):
        ci = CoinInput(parentid=parentid, fulfillment=fulfillment)
        ci.parent_output = parent_output
        self._coin_inputs.append(ci)

    def refund_coin_output_set(self, value, condition, id=None):
        co = CoinOutput(value=value, condition=condition)
        co.id = id
        self._refund_coin_output = co

    @property
    def registration_fee(self):
        return self._registration_fee

    @registration_fee.setter
    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)))

    @property
    def transaction_fee(self):
        if self._transaction_fee is None:
            return Currency()
        return self._transaction_fee

    @transaction_fee.setter
    def transaction_fee(self, txfee):
        if txfee is None:
            self._transaction_fee = None
        else:
            self._transaction_fee = Currency(value=txfee)

    @property
    def miner_fees(self):
        if self._transaction_fee is None:
            return []
        return [self._transaction_fee]

    def signature_add(self, public_key, signature):
        """
        Implements SignatureCallbackBase.
        """
        if self._public_key.unlockhash != public_key.unlockhash:
            raise ValueError(
                "given public key ({}) does not equal public key ({})".format(
                    str(self._public_key.unlockhash),
                    str(public_key.unlockhash)))
        self.signature = signature

    def _signature_hash_input_get(self, *extra_objects):
        e = encoder_rivine_get()

        # encode the transaction version
        e.add_int8(self.version)

        # encode the specifier
        e.add_array(TransactionV210._SPECIFIER)

        # encode the public key
        e.add_all(self.public_key)

        # extra objects if any
        if extra_objects:
            e.add_all(*extra_objects)

        # encode coin inputs
        e.add(len(self.coin_inputs))
        for ci in self.coin_inputs:
            e.add(ci.parentid)

        # encode registration and transaction fee
        e.add_all(self.registration_fee, self.transaction_fee)

        # encode refund coin output
        if self._refund_coin_output is None:
            e.add_int8(0)
        else:
            e.add_int8(1)
            e.add(self._refund_coin_output)

        # return data
        return e.data

    def _id_input_compute(self):
        return bytearray(
            TransactionV210._SPECIFIER) + self._binary_encode_data()

    def _binary_encode_data(self):
        e = encoder_rivine_get()
        # encode all easy properties
        e.add_all(self.public_key, self.signature, self.registration_fee,
                  self.transaction_fee, self.coin_inputs)
        # encode the only "pointer" property
        if self._refund_coin_output is None:
            e.add_int8(0)
        else:
            e.add_int8(1)
            e.add(self._refund_coin_output)

        # return encoded data
        return e.data

    def _from_json_data_object(self, data):
        # decode public key
        if 'pubkey' in data:
            self._public_key = PublicKey.from_json(data['pubkey'])
        else:
            self._public_key = None
        # decode signature
        if 'signature' in data:
            self._signature = ED25519Signature.from_json(data['signature'])
        else:
            self._signature = None
        # decode registration fee
        if 'regfee' in data:
            self.registration_fee = Currency.from_json(data['regfee'])
        else:
            self.registration_fee = None
        # decode transaction fee
        if 'txfee' in data:
            self._transaction_fee = Currency.from_json(data['txfee'])
        else:
            self._transaction_fee = None
        # decode coin inputs
        self._coin_inputs = [
            CoinInput.from_json(ci) for ci in data.get('coininputs', []) or []
        ]
        # decode refund coin output (if it exists)
        if 'refundcoinoutput' in data:
            self._refund_coin_output = CoinOutput.from_json(
                data['refundcoinoutput'])
        else:
            self._refund_coin_output = None

    def _json_data_object(self):
        tftaddress = self.public_key.unlockhash
        erc20address = ERC20Address.from_unlockhash(tftaddress)
        output = {
            'pubkey': self.public_key.json(),
            'tftaddress': tftaddress.json(),
            'erc20address': erc20address.json(),
            'signature': self.signature.json(),
            'regfee': self.registration_fee.json(),
            'txfee': self.transaction_fee.json(),
            'coininputs': [ci.json() for ci in self.coin_inputs],
        }
        if self._refund_coin_output is not None:
            output['refundcoinoutput'] = self._refund_coin_output.json()
        return output

    def _extra_signature_requests_new(self):
        if self._public_key is None:
            # if no parent public key is defined, cannot do anything
            return []
        if self._signature is not None:
            return []  # nothing to do
        # generate the input hash func
        input_hash_func = InputSignatureHashFactory(
            self, TransactionV210.SPECIFIER_REGISTRATION_SIGNATURE
        ).signature_hash_new
        # define the input_hash_new generator function,
        # used to create the input hash for creating the signature
        unlockhash = self._public_key.unlockhash

        def input_hash_gen(public_key):
            return input_hash_func()

        # create the only signature request
        return [
            SignatureRequest(unlockhash=unlockhash,
                             input_hash_gen=input_hash_gen,
                             callback=self)
        ]

    def _extra_is_fulfilled(self):
        return self._signature is not None
Exemplo n.º 16
0
    def legacy_from_json(cls, obj):
        """
        Class method to decode v1 Tx from a legacy v0 Tx.
        """

        tv = obj.get('version', -1)
        if TransactionVersion.LEGACY != tv:
            raise ValueError("legacy v0 transaction is expected to be of version {}, not version {}".format(
                TransactionVersion.LEGACY, tv))
        txn = cls()

        if 'data' not in obj:
            raise ValueError("no data object found in Legacy Transaction (v{})".format(
                TransactionVersion.LEGACY))
        txn_data = obj['data']
        if 'coininputs' in txn_data:
            for legacy_ci_info in (txn_data['coininputs'] or []):
                unlocker = legacy_ci_info.get('unlocker', {})
                ci_info = {
                    'parentid': legacy_ci_info.get('parentid', ''),
                    'fulfillment': {
                        'type': 1,
                        'data': {
                            'publickey': unlocker.get('condition', {}).get('publickey'),
                            'signature': unlocker.get('fulfillment', {}).get('signature'),
                        }
                    }
                }
                ci = CoinInput.from_json(ci_info)
                txn._coin_inputs.append(ci)
        if 'coinoutputs' in txn_data:
            for legacy_co_info in (txn_data['coinoutputs'] or []):
                co_info = {
                    'value': legacy_co_info.get('value', '0'),
                    'condition': {
                        'type': 1,
                        'data': {
                            'unlockhash': legacy_co_info.get('unlockhash', ''),
                        }
                    }
                }
                co = CoinOutput.from_json(co_info)
                txn._coin_outputs.append(co)
        if 'blockstakeinputs' in txn_data:
            for legacy_bsi_info in (txn_data['blockstakeinputs'] or []):
                unlocker = legacy_bsi_info.get('unlocker', {})
                bsi_info = {
                    'parentid': legacy_bsi_info.get('parentid', ''),
                    'fulfillment': {
                        'type': 1,
                        'data': {
                            'publickey': unlocker.get('condition', {}).get('publickey'),
                            'signature': unlocker.get('fulfillment', {}).get('signature'),
                        }
                    }
                }
                bsi = BlockstakeInput.from_json(bsi_info)
                txn._blockstake_inputs.append(bsi)
        if 'blockstakeoutputs' in txn_data:
            for legacy_bso_info in (txn_data['blockstakeoutputs'] or []):
                bso_info = {
                    'value': legacy_bso_info.get('value', '0'),
                    'condition': {
                        'type': 1,
                        'data': {
                            'unlockhash': legacy_bso_info.get('unlockhash', ''),
                        }
                    }
                }
                bso = BlockstakeOutput.from_json(bso_info)
                txn._blockstake_outputs.append(bso)

        if 'minerfees' in txn_data:
            for miner_fee in (txn_data['minerfees'] or []):
                txn._miner_fees.append(Currency.from_json(miner_fee))
        if 'arbitrarydata' in txn_data:
            txn._data = BinaryData.from_json(txn_data.get(
                'arbitrarydata', None) or '', strencoding='base64')

        txn._legacy = True
        return txn