Exemplo n.º 1
0
def tx_or_block_from_proto(
        tx_proto: protos.BaseTransaction,
        storage: Optional['TransactionStorage'] = None) -> 'BaseTransaction':
    from hathor.transaction.block import Block
    from hathor.transaction.merge_mined_block import MergeMinedBlock
    from hathor.transaction.token_creation_tx import TokenCreationTransaction
    from hathor.transaction.transaction import Transaction
    if tx_proto.HasField('transaction'):
        return Transaction.create_from_proto(tx_proto, storage=storage)
    elif tx_proto.HasField('block'):
        if tx_proto.block.HasField('aux_pow'):
            return MergeMinedBlock.create_from_proto(tx_proto, storage=storage)
        else:
            return Block.create_from_proto(tx_proto, storage=storage)
    elif tx_proto.HasField('tokenCreationTransaction'):
        return TokenCreationTransaction.create_from_proto(tx_proto,
                                                          storage=storage)
    else:
        raise ValueError('invalid base_transaction_oneof')
Exemplo n.º 2
0
    def sign_transaction(self, tx: Transaction,
                         tx_storage: 'TransactionStorage') -> None:
        """Signs a transaction. Iterates over all inputs and signs the ones belonging to this wallet.

        :param tx: transaction to sign
        :type tx: py:class:`hathor.transaction.Transaction`

        :param tx_storage: storage to search for output tx
        :type tx_storage: TransactionStorage

        :return: there's no return. This function modifies the tx given to it
        :rtype: None
        """
        data_to_sign = tx.get_sighash_all(clear_input_data=True)

        for _input, address58 in self.match_inputs(tx.inputs, tx_storage):
            if address58:
                public_key_bytes, signature = self.get_input_aux_data(
                    data_to_sign, self.get_private_key(address58))
                _input.data = P2PKH.create_input_data(public_key_bytes,
                                                      signature)
Exemplo n.º 3
0
def generate_signature(tx: Transaction,
                       private_key_bytes: bytes,
                       password: Optional[bytes] = None) -> bytes:
    """ Create a signature for the tx

        :param tx: transaction with the data to be signed
        :type tx: :py:class:`hathor.transaction.transaction.Transaction`

        :param private_key_bytes: private key to generate the signature
        :type private_key_bytes: bytes

        :param password: password to decrypt the private key
        :type password: bytes

        :return: signature of the tx
        :rtype: bytes
    """
    private_key = get_private_key_from_bytes(private_key_bytes,
                                             password=password)
    data_to_sign = tx.get_sighash_all()
    hashed_data = hashlib.sha256(data_to_sign).digest()
    signature = private_key.sign(hashed_data, ec.ECDSA(hashes.SHA256()))
    return signature
Exemplo n.º 4
0
    def on_tx_winner(self, tx: Transaction) -> None:
        """ This method is called when a tx is set as winner of a conflict
            We use it to update the balance and the variable that stores it
            We check it's outputs and inputs to update wallet information

            For outputs we have the following situations:

            . In case it's in the spent or unspent we do nothing
            . In case is not found, it was deleted because of a previous conflict, so we recreate in the unspent

            For inputs we have the following situations:

            . If it's in the unspent_txs, we remove from there and add in the spent_txs
            . If it's in the spent_txs, we do nothing
            . If it's not in both, we add in the spent_txs

            :param tx: Transaction that was voided
            :type tx: :py:class:`hathor.transaction.Transaction`
        """
        assert tx.hash is not None
        assert tx.storage is not None

        should_update = False
        # check outputs
        for index, output in enumerate(tx.outputs):
            script_type_out = parse_address_script(output.script)
            token_id = tx.get_token_uid(output.get_token_index())
            if script_type_out:
                if script_type_out.address in self.keys:
                    # Find output
                    key = (tx.hash, index)
                    utxo = self.unspent_txs[token_id].get(key)
                    if utxo is None:
                        utxo = self.maybe_spent_txs[token_id].get(key)
                    if not utxo:
                        # Not found in unspent
                        # Try to find in spent tx
                        if key not in self.spent_txs or len(
                                self.spent_txs[key]) == 0:
                            # If it's not in unspet or spent it was deleted, so we create again in the unspent
                            utxo = UnspentTx(tx.hash,
                                             index,
                                             output.value,
                                             tx.timestamp,
                                             script_type_out.address,
                                             output.token_data,
                                             timelock=script_type_out.timelock)
                            self.unspent_txs[token_id][key] = utxo

                    # Remove from voided_unspent, if it's there
                    voided_utxo = self.voided_unspent.pop(key, None)
                    if voided_utxo:
                        # If it's there, we should update
                        should_update = True

        # check inputs
        for _input in tx.inputs:
            output_tx = tx.storage.get_transaction(_input.tx_id)
            output = output_tx.outputs[_input.index]
            token_id = output_tx.get_token_uid(output.get_token_index())

            script_type_out = parse_address_script(output.script)
            if script_type_out:
                if script_type_out.address in self.keys:
                    key = (_input.tx_id, _input.index)
                    # Remove from voided_spent, if it's there
                    # First try to find it in voided_spent
                    voided_stxi_list = self.voided_spent.get(key, [])
                    list_index = -1
                    for i, spent in enumerate(voided_stxi_list):
                        if (spent.tx_id == tx.hash
                                and spent.from_index == _input.index
                                and spent.from_tx_id == _input.tx_id):
                            list_index = i
                            break
                    if list_index > -1:
                        # If it's there, we remove it
                        self.voided_spent[key].pop(list_index)
                        should_update = True

                    # Remove from unspent_txs, if it's there
                    old_utxo = self.unspent_txs[token_id].pop(key, None)
                    if old_utxo is None:
                        old_utxo = self.maybe_spent_txs[token_id].pop(
                            key, None)
                    if old_utxo:
                        # add to spent_txs
                        spent = SpentTx(tx.hash, _input.tx_id, _input.index,
                                        old_utxo.value, tx.timestamp)
                        self.spent_txs[(_input.tx_id,
                                        _input.index)].append(spent)
                        should_update = True
                        continue

                    # If we dont have it in the unspent_txs, we check in the spent txs
                    # Try to find in spent tx
                    found = False
                    if key in self.spent_txs:
                        list_index = -1
                        for i, spent in enumerate(self.spent_txs[key]):
                            if (spent.tx_id == tx.hash
                                    and spent.from_index == _input.index
                                    and spent.from_tx_id == _input.tx_id):
                                list_index = i
                                break

                        if list_index > -1:
                            found = True

                    if not found:
                        # If spent not found, we recreate it
                        # Get tx from output to get the value
                        output_tx = tx.storage.get_transaction(_input.tx_id)
                        output = output_tx.outputs[_input.index]

                        spent = SpentTx(tx.hash, _input.tx_id, _input.index,
                                        output.value, tx.timestamp)
                        self.spent_txs[key].append(spent)
                        should_update = True

        if should_update:
            # update balance
            self.update_balance()
            # publish update history
            self.publish_update(HathorEvents.WALLET_HISTORY_UPDATED)
Exemplo n.º 5
0
    def on_tx_voided(self, tx: Transaction) -> None:
        """ This method is called when a tx is voided in a conflict
            We use it to update the balance and the variable that stores it
            We check it's outputs and inputs to update wallet information

            For outputs we have the following situations:

            . Not used yet, so it's still in the unspent_txs, we remove it from there and decrease the balance
            . Already used, so it's in spent_txs, we remove from there
            . Not found anywhere, so it was already updated in another conflict resolution

            For inputs we have the following situations:

            . If it's in the unspent_txs, we have to do nothing
            . If it's in the spent_txs, we remove from the array. If this was the last tx, we recreate in the unspent

            :param tx: Transaction that was voided
            :type tx: :py:class:`hathor.transaction.Transaction`
        """
        assert tx.hash is not None
        assert tx.storage is not None

        should_update = False

        # check outputs
        for index, tx_output in enumerate(tx.outputs):
            script_type_out = parse_address_script(tx_output.script)
            token_id = tx.get_token_uid(tx_output.get_token_index())
            if script_type_out:
                if script_type_out.address in self.keys:
                    # Remove this output from unspent_tx, if still there
                    key = (tx.hash, index)
                    utxo = self.unspent_txs[token_id].pop(key, None)
                    if utxo is None:
                        utxo = self.maybe_spent_txs[token_id].pop(key, None)
                    if utxo:
                        # Output found: update balance
                        should_update = True
                    else:
                        # If it is in spent tx, remove from dict
                        if key in self.spent_txs:
                            should_update = True
                            del self.spent_txs[key]

                    # Save in voided_unspent, if it's not there yet
                    # First try to find it in voided_unspent
                    voided_utxo = self.voided_unspent.get(key, None)
                    if not voided_utxo:
                        # If it's not there, we add it
                        voided = UnspentTx(tx.hash,
                                           index,
                                           tx_output.value,
                                           tx.timestamp,
                                           script_type_out.address,
                                           tx_output.token_data,
                                           voided=True,
                                           timelock=script_type_out.timelock)
                        self.voided_unspent[key] = voided
                        should_update = True

        # check inputs
        for _input in tx.inputs:
            output_tx = tx.storage.get_transaction(_input.tx_id)
            output_ = output_tx.outputs[_input.index]
            script_type_out = parse_address_script(output_.script)
            token_id = output_tx.get_token_uid(output_.get_token_index())
            if script_type_out:
                if script_type_out.address in self.keys:
                    output: Optional[TxOutput] = None
                    # Try to find in spent tx
                    key = (_input.tx_id, _input.index)
                    if key in self.spent_txs:
                        list_index = -1
                        for i, spent in enumerate(self.spent_txs[key]):
                            if (spent.tx_id == tx.hash
                                    and spent.from_index == _input.index
                                    and spent.from_tx_id == _input.tx_id):
                                list_index = i
                                break

                        if list_index > -1:
                            # Spent found: remove from list
                            spent = self.spent_txs[key].pop(list_index)

                            if len(self.spent_txs[key]) == 0:
                                # If this was the last input that spent this output, we recreate the output
                                output_tx = tx.storage.get_transaction(
                                    spent.from_tx_id)
                                output = output_tx.outputs[spent.from_index]
                                assert output is not None

                                script_type_out = parse_address_script(
                                    output.script)
                                if script_type_out and script_type_out.address in self.keys:
                                    utxo = UnspentTx(
                                        _input.tx_id,
                                        _input.index,
                                        output.value,
                                        output_tx.timestamp,
                                        script_type_out.address,
                                        output.token_data,
                                        timelock=script_type_out.timelock)
                                    self.unspent_txs[token_id][key] = utxo

                            should_update = True

                    # Save in voided_spent, if it's not there yet
                    # First try to find it in voided_spent
                    voided_stxi_list = self.voided_spent.get(key, [])
                    list_index = -1
                    for i, spent in enumerate(voided_stxi_list):
                        if (spent.tx_id == tx.hash
                                and spent.from_index == _input.index
                                and spent.from_tx_id == _input.tx_id):
                            list_index = i
                            break
                    if list_index == -1:
                        # If it's not there, we add it
                        if output is None:
                            output_tx = tx.storage.get_transaction(
                                _input.tx_id)
                            output = output_tx.outputs[_input.index]

                        voided_spent = SpentTx(tx.hash,
                                               _input.tx_id,
                                               _input.index,
                                               output.value,
                                               tx.timestamp,
                                               voided=True)
                        self.voided_spent[key].append(voided_spent)
                        should_update = True

        if should_update:
            # update balance
            self.update_balance()
            # publish update history
            self.publish_update(HathorEvents.WALLET_HISTORY_UPDATED)