Beispiel #1
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)))
Beispiel #2
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