def test_double_deposit_same_tx(dbsession, eth_network_id, eth_service, eth_asset_id):
    """Check that we have some logic to avoid depositing the same asset twice."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        dbsession.flush()
        op_id = op.id

    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        assert op.id == op_id
        assert dbsession.query(CryptoOperation).count() == 1
Beispiel #2
0
def test_double_deposit_same_tx(dbsession, eth_network_id, eth_service,
                                eth_asset_id):
    """Check that we have some logic to avoid depositing the same asset twice."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        dbsession.flush()
        op_id = op.id

    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        assert op.id == op_id
        assert dbsession.query(CryptoOperation).count() == 1
Beispiel #3
0
 def close_withdraw():
     # Fill in details.
     # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation.
     op = dbsession.query(CryptoOperation).get(opid)
     op.txid = txid_to_bin(txid)
     op.block = None
     op.mark_broadcasted()
Beispiel #4
0
def test_event_claim_fees(web3, topped_up_hosted_wallet, coinbase):
    """We correctly can claim transaction fees from the hosted wallet contract."""

    hosted_wallet = topped_up_hosted_wallet
    coinbase_address = coinbase

    # Do a withdraw to cause some fees
    listener, events = create_contract_listener(hosted_wallet.contract)
    assert hosted_wallet.get_balance() > TEST_VALUE
    txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE)
    confirm_transaction(web3, txid)

    # Claim fees for the withdraw operation
    claim_txid, price = hosted_wallet.claim_fees(txid)
    confirm_transaction(web3, claim_txid)

    # We should have event for withdraw + claims
    update_count = listener.poll()
    assert update_count == 2
    assert len(events) == 2
    event_name, input_data = events[-1]  # Fee claim event

    assert event_name == "ClaimFee"
    assert input_data["txid"] == txid_to_bin(
        txid)  # This was correctly targeted to original withdraw
    assert input_data["value"] == to_wei(price)  # We claimed correct amount
Beispiel #5
0
 def close_withdraw():
     # Fill in details.
     # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation.
     op = dbsession.query(CryptoOperation).get(opid)
     op.txid = txid_to_bin(txid)
     op.block = None
     op.mark_broadcasted()
    def handle_event(self, event_name: str, contract_address: str,
                     log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        opid = self.get_unique_transaction_id(log_entry)

        existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
        if existing_op:
            # Already in the database, all we need to do is to call blocknumber updater now
            return False

        network = self.dbsession.query(AssetNetwork).get(self.network_id)
        address = self.dbsession.query(CryptoAddress).filter_by(
            address=eth_address_to_bin(contract_address),
            network=network).one()

        op = self.create_op(event_name, address, opid, log_data, log_entry)
        if not op:
            # This was an event we don't care about
            return False

        op.opid = opid
        op.txid = txid_to_bin(log_entry["transactionHash"])
        op.block = int(log_entry["blockNumber"], 16)
        op.required_confirmation_count = self.confirmation_count
        self.dbsession.add(op)

        self.notify_deposit(op)

        return True
Beispiel #7
0
    def perform_tx():
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Create Tonex proxy object
        token = Token.create_token(web3,
                                   name=asset.name,
                                   symbol=asset.symbol,
                                   supply=asset.supply,
                                   owner=address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(token.initial_txid)
        op.block = None
        op.external_address = eth_address_to_bin(token.address)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
    def handle_event(self, event_name: str, contract_address: str, log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        with self._get_tm():
            opid = self.get_unique_transaction_id(log_entry)

            existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
            if existing_op:
                # Already in the database, all we need to do is to call blocknumber updater now
                return False

            network = self.dbsession.query(AssetNetwork).get(self.network_id)
            address = self.dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(contract_address), network=network).one()

            op = self.create_op(event_name, address, opid, log_data, log_entry)
            if not op:
                # This was an event we don't care about
                return False

            op.opid = opid
            op.txid = txid_to_bin(log_entry["transactionHash"])
            op.block = int(log_entry["blockNumber"], 16)
            op.required_confirmation_count = self.confirmation_count
            self.dbsession.add(op)

            self.notify_deposit(op)

            return True
Beispiel #9
0
    def perform_tx():
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Create Tonex proxy object
        token = Token.create_token(web3, name=asset.name, symbol=asset.symbol, supply=asset.supply, owner=address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(token.initial_txid)
        op.block = None
        op.external_address = eth_address_to_bin(token.address)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
    def update_tx(self, current_block: int, txinfo: dict, receipt: dict) -> Tuple[int, int]:
        """Process logs from initial log run or filter updates.

        :return: (performed updates, failed updates)
        """

        # We may have multiple ops for one transaction
        ops = self.dbsession.query(CryptoOperation).filter_by(txid=txid_to_bin(receipt["transactionHash"]))
        updates = failures = 0

        for op in ops:

            # http://ethereum.stackexchange.com/q/6007/620
            if txinfo["gas"] == receipt["gasUsed"]:
                op.other_info["failure_reason"] = "Smart contract rejected the transaction"
                op.mark_failure()
                failures += 1

            # Withdraw operation has not gets it block yet
            # Block number may change because of the works
            assert receipt["blockNumber"].startswith("0x")
            op.block = int(receipt["blockNumber"], 16)

            confirmation_count = current_block - op.block
            if op.update_confirmations(confirmation_count):

                # Notify listeners we reached the goal
                logger.info("Completed, confirmations reached %s", op)
                self.registry.notify(CryptoOperationCompleted(op, self.registry, self.web3))

                updates += 1

        return updates, failures
    def handle_event(self, event_name: str, contract_address: str,
                     log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        opid = self.get_unique_transaction_id(log_entry)

        existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
        if existing_op:
            # Already in the database, all we need to do is to call blocknumber updater now
            return False

        network = self.dbsession.query(AssetNetwork).get(self.network_id)
        asset = self.dbsession.query(Asset).filter_by(
            network=network,
            external_id=eth_address_to_bin(contract_address)).one()

        if event_name == "Transfer":

            to_address = eth_address_to_bin(log_data["to"])
            from_address = eth_address_to_bin(log_data["from"])
            value = Decimal(log_data["value"])

            self.logger.debug("Incoming transfer event %s %s %s", from_address,
                              to_address, value)

            # Get destination address entry
            address = self.dbsession.query(CryptoAddress).filter_by(
                address=to_address).one_or_none()
            if not address:
                # Address not in our system
                return False

            # Create operation
            op = CryptoAddressDeposit(network=network)
            op.opid = opid
            op.txid = txid_to_bin(log_entry["transactionHash"])
            op.external_address = from_address
            op.block = int(log_entry["blockNumber"], 16)
            op.required_confirmation_count = self.confirmation_count
            op.crypto_account = address.get_or_create_account(asset)

            # Create holding account that keeps the value until we receive N amount of confirmations
            acc = Account(asset=asset)
            self.dbsession.add(acc)
            self.dbsession.flush()

            acc.do_withdraw_or_deposit(
                value, "Token {} deposit from {} in tx {}".format(
                    asset.symbol, log_data["from"],
                    log_entry["transactionHash"]))
            op.holding_account = acc
            self.dbsession.add(op)

            self.notify_deposit(op)

            return True
        else:
            # Unmonitored event
            return False
    def get_unique_transaction_id(self, log_entry: dict) -> bytes:
        """Get txid - logindex pair.

        Because ethereum transactions may contain several log events due to cross contract calls, it's not enough to identify events by their transaction hash. Instead, we use tranaction hash - log index pair."""
        txid = txid_to_bin(log_entry["transactionHash"])
        logIndex = int(log_entry["logIndex"], 16)
        assert logIndex < 256
        data = txid + bytes([logIndex])
        assert len(data) < 34
        return data
    def get_unique_transaction_id(self, log_entry: dict) -> bytes:
        """Get txid - logindex pair.

        Because ethereum transactions may contain several log events due to cross contract calls, it's not enough to identify events by their transaction hash. Instead, we use tranaction hash - log index pair."""
        txid = txid_to_bin(log_entry["transactionHash"])
        logIndex = int(log_entry["logIndex"], 16)
        assert logIndex < 256
        data = txid + bytes([logIndex])
        assert len(data) < 34
        return data
    def handle_event(self, event_name: str, contract_address: str, log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        with self._get_tm():

            opid = self.get_unique_transaction_id(log_entry)

            existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
            if existing_op:
                # Already in the database, all we need to do is to call blocknumber updater now
                return False

            network = self.dbsession.query(AssetNetwork).get(self.network_id)
            asset = self.dbsession.query(Asset).filter_by(network=network, external_id=eth_address_to_bin(contract_address)).one()

            if event_name == "Transfer":

                to_address = eth_address_to_bin(log_data["to"])
                from_address = eth_address_to_bin(log_data["from"])
                value = Decimal(log_data["value"])

                # Get destination address entry
                address = self.dbsession.query(CryptoAddress).filter_by(address=to_address).one_or_none()
                if not address:
                    # Address not in our system
                    return False

                # Create operation
                op = CryptoAddressDeposit(network=network)
                op.opid = opid
                op.txid = txid_to_bin(log_entry["transactionHash"])
                op.external_address = from_address
                op.block = int(log_entry["blockNumber"], 16)
                op.required_confirmation_count = self.confirmation_count
                op.crypto_account = address.get_or_create_account(asset)

                # Create holding account that keeps the value until we receive N amount of confirmations
                acc = Account(asset=asset)
                self.dbsession.add(acc)
                self.dbsession.flush()

                acc.do_withdraw_or_deposit(value, "Token {} deposit from {} in tx {}".format(asset.symbol, log_data["from"], log_entry["transactionHash"]))
                op.holding_account = acc
                self.dbsession.add(op)

                self.notify_deposit(op)

                return True
            else:
                # Unmonitored event
                return False
Beispiel #15
0
    def finish_op():
        op = dbsession.query(CryptoOperation).get(opid)
        txid = wallet.initial_txid
        receipt = web3.eth.getTransactionReceipt(txid)

        op.txid = txid_to_bin(txid)
        op.block = receipt["blockNumber"]
        op.address.address = eth_address_to_bin(wallet.address)
        op.external_address = op.address.address

        # There is no broadcast wait, so we can close this right away
        op.mark_performed()
        op.mark_broadcasted()
        op.mark_complete()
Beispiel #16
0
    def finish_op():
        op = dbsession.query(CryptoOperation).get(opid)
        txid = wallet.initial_txid
        receipt = web3.eth.getTransactionReceipt(txid)

        op.txid = txid_to_bin(txid)
        op.block = receipt["blockNumber"]
        op.address.address = eth_address_to_bin(wallet.address)
        op.external_address = op.address.address

        # There is no broadcast wait, so we can close this right away
        op.mark_performed()
        op.mark_broadcasted()
        op.mark_complete()
Beispiel #17
0
def do_faux_deposit(address: CryptoAddress, asset_id, amount) -> CryptoAddressDeposit:
    """Simulate deposit to address."""

    network = address.network
    txid = network.other_data["test_txid_pool"].pop()
    # txid = "0x00df829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf"
    dbsession = Session.object_session(address)
    asset = dbsession.query(Asset).get(asset_id)
    txid = txid_to_bin(txid)

    op = address.deposit(Decimal(amount), asset, txid, bin_to_txid(txid))
    op.required_confirmation_count = 1
    op.external_address = address.address
    dbsession.add(op)
    dbsession.flush()
    return op
Beispiel #18
0
def do_faux_deposit(address: CryptoAddress, asset_id,
                    amount) -> CryptoAddressDeposit:
    """Simulate deposit to address."""

    network = address.network
    txid = network.other_data["test_txid_pool"].pop()
    # txid = "0x00df829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf"
    dbsession = Session.object_session(address)
    asset = dbsession.query(Asset).get(asset_id)
    txid = txid_to_bin(txid)

    op = address.deposit(Decimal(amount), asset, txid, bin_to_txid(txid))
    op.required_confirmation_count = 1
    op.external_address = address.address
    dbsession.add(op)
    dbsession.flush()
    return op
Beispiel #19
0
def _create_address(web3, dbsession, opid):

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        txid = TEST_ADDRESS_INITIAL_TXID

        # Deterministically pull faux addresses from pool
        network = op.network
        address_pool = network.other_data["test_address_pool"]
        address = address_pool.pop()

        op.txid = txid_to_bin(txid)
        op.block = 666
        op.address.address = eth_address_to_bin(address)
        op.external_address = op.address.address

        op.mark_performed()
        op.mark_broadcasted()
Beispiel #20
0
def _create_address(web3, dbsession, opid):

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        txid = TEST_ADDRESS_INITIAL_TXID

        # Deterministically pull faux addresses from pool
        network = op.network
        address_pool = network.other_data["test_address_pool"]
        address = address_pool.pop()

        op.txid = txid_to_bin(txid)
        op.block = 666
        op.address.address = eth_address_to_bin(address)
        op.external_address = op.address.address

        op.mark_performed()
        op.mark_broadcasted()
Beispiel #21
0
def test_deposit_eth_account(dbsession, eth_network_id, eth_service,
                             eth_asset_id):
    """Deposit Ethereums to an account."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        opid = op.id

    # Resolve deposit op
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1
    assert failed_op_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        op.resolve()  # Force resolution regardless on confirmation count

    # Check balances are settled
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_account(asset)
        op = dbsession.query(CryptoOperation).one()
        assert account.account.get_balance() == Decimal(10)
        assert op.holding_account.get_balance() == 0

        # Transaction label should be the Ethereum txid
        tx = account.account.transactions.one()
        assert tx.message == TEST_TXID
def test_deposit_eth_account(dbsession, eth_network_id, eth_service, eth_asset_id):
    """Deposit Ethereums to an account."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        opid = op.id

    # Resolve deposit op
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1
    assert failed_op_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        op.resolve()  # Force resolution regardless on confirmation count

    # Check balances are settled
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_account(asset)
        op = dbsession.query(CryptoOperation).one()
        assert account.account.get_balance() == Decimal(10)
        assert op.holding_account.get_balance() == 0

        # Transaction label should be the Ethereum txid
        tx = account.account.transactions.one()
        assert tx.message == TEST_TXID
Beispiel #23
0
def _withdraw(web3, dbsession: Session, opid: UUID):

    with transaction.manager:
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator

        op.mark_performed()  # Don't try to pick this op automatically again

        # Fill in details.
        # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation.
        op = dbsession.query(CryptoOperation).get(opid)
        op.txid = txid_to_bin(TEST_ADDRESS_INITIAL_TXID)
        op.block = None
        op.mark_broadcasted()
Beispiel #24
0
def _withdraw(web3, dbsession: Session, opid: UUID):

    with transaction.manager:
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator

        op.mark_performed()  # Don't try to pick this op automatically again

        # Fill in details.
        # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation.
        op = dbsession.query(CryptoOperation).get(opid)
        op.txid = txid_to_bin(TEST_ADDRESS_INITIAL_TXID)
        op.block = None
        op.mark_broadcasted()
Beispiel #25
0
    def update_tx(self, current_block: int, txinfo: dict, receipt: dict) -> Tuple[int, int]:
        """Process logs from initial log run or filter updates.

        :return: (performed updates, failed updates)
        """

        # We may have multiple ops for one transaction
        ops = self.dbsession.query(CryptoOperation).filter_by(txid=txid_to_bin(receipt["transactionHash"]))
        updates = failures = 0

        for op in ops:

            # http://ethereum.stackexchange.com/q/6007/620
            if txinfo["gas"] == receipt["gasUsed"]:
                op.mark_failed("Smart contract rejected the transaction")
                failures += 1
                continue

            failure_reason = self.check_bad_hosted_wallet_events(op, receipt)
            if failure_reason:
                op.mark_failed(failure_reason)
                failures += 1
                continue

            # Withdraw operation has not gets it block yet
            # Block number may change because of the works
            assert receipt["blockNumber"].startswith("0x")
            op.block = int(receipt["blockNumber"], 16)

            confirmation_count = current_block - op.block
            if op.update_confirmations(confirmation_count):
                # Notify listeners we reached the goal
                logger.info("Completed, confirmations reached %s", op)
                self.registry.notify(CryptoOperationCompleted(op, self.registry, self.web3))
                updates += 1

        return updates, failures
Beispiel #26
0
def _create_token(web3, dbsession, opid):
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(TEST_TOKEN_TXID)
        op.block = None
        op.external_address = eth_address_to_bin(TEST_TOKEN_ADDRESS)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
Beispiel #27
0
def _create_token(web3, dbsession, opid):
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(TEST_TOKEN_TXID)
        op.block = None
        op.external_address = eth_address_to_bin(TEST_TOKEN_ADDRESS)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
Beispiel #28
0
def test_withdraw_eth_account(dbsession, eth_service, eth_network_id,
                              eth_asset_id):
    """Withdraw ETHs to an address."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.flush()

        assert address.id
        assert address.address
        asset = dbsession.query(Asset).get(eth_asset_id)

        # Create an account of ETH tokens on that address
        ca_account = address.create_account(asset)

    # It should have zero balance by default
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.asset_id == eth_asset_id
        assert ca_account.account.get_balance() == Decimal(0)

    # Faux top up so we have value to withdraw
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.do_withdraw_or_deposit(
            Decimal("+10"), "Faux top up")

    # Create withdraw operations
    withdraw_address = eth_address_to_bin(TEST_ADDRESS)
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        op = ca_account.withdraw(Decimal("10"), withdraw_address,
                                 "Bailing out")

        # We withdraw 10 ETHs
        assert op.holding_account.get_balance() == Decimal("10")
        assert op.holding_account.asset == dbsession.query(Asset).get(
            eth_asset_id)
        assert op.holding_account.transactions.count() == 1
        assert op.holding_account.transactions.first().message == "Bailing out"

        # Check all looks good on sending account
        assert ca_account.account.transactions.count() == 2
        assert ca_account.account.transactions.all(
        )[0].message == "Faux top up"
        assert ca_account.account.transactions.all(
        )[1].message == "Bailing out"
        assert ca_account.account.get_balance() == 0

    def _withdraw_eth(service, dbsession, opid):
        # Mocked withdraw op that always success
        with transaction.manager:
            op = dbsession.query(CryptoOperation).get(opid)
            op.txid = txid_to_bin(TEST_TXID)
            op.mark_complete()

    with mock.patch("websauna.wallet.ethereum.ops.withdraw_eth",
                    new=_withdraw_eth):
        success_op_count, failed_op_count = eth_service.run_waiting_operations(
        )

    # Check that operations have been marked as success
    with transaction.manager:
        ops = list(dbsession.query(CryptoOperation).all())
        assert len(ops) == 1
        assert isinstance(ops[0], CryptoAddressWithdraw)
        assert ops[0].state == CryptoOperationState.success
        assert ops[0].txid == txid_to_bin(TEST_TXID)
 def _withdraw_eth(service, dbsession, opid):
     # Mocked withdraw op that always success
     with transaction.manager:
         op = dbsession.query(CryptoOperation).get(opid)
         op.txid = txid_to_bin(TEST_TXID)
         op.mark_complete()
def test_withdraw_eth_account(dbsession, eth_service, eth_network_id, eth_asset_id):
    """Withdraw ETHs to an address."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.flush()

        assert address.id
        assert address.address
        asset = dbsession.query(Asset).get(eth_asset_id)

        # Create an account of ETH tokens on that address
        ca_account = address.create_account(asset)

    # It should have zero balance by default
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.asset_id == eth_asset_id
        assert ca_account.account.get_balance() == Decimal(0)

    # Faux top up so we have value to withdraw
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.do_withdraw_or_deposit(Decimal("+10"), "Faux top up")

    # Create withdraw operations
    withdraw_address = eth_address_to_bin(TEST_ADDRESS)
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        op = ca_account.withdraw(Decimal("10"), withdraw_address, "Bailing out")

        # We withdraw 10 ETHs
        assert op.holding_account.get_balance() == Decimal("10")
        assert op.holding_account.asset == dbsession.query(Asset).get(eth_asset_id)
        assert op.holding_account.transactions.count() == 1
        assert op.holding_account.transactions.first().message == "Bailing out"

        # Check all looks good on sending account
        assert ca_account.account.transactions.count() == 2
        assert ca_account.account.transactions.all()[0].message == "Faux top up"
        assert ca_account.account.transactions.all()[1].message == "Bailing out"
        assert ca_account.account.get_balance() == 0

    def _withdraw_eth(service, dbsession, opid):
        # Mocked withdraw op that always success
        with transaction.manager:
            op = dbsession.query(CryptoOperation).get(opid)
            op.txid = txid_to_bin(TEST_TXID)
            op.mark_complete()

    with mock.patch("websauna.wallet.ethereum.ops.withdraw_eth", new=_withdraw_eth):
        success_op_count, failed_op_count = eth_service.run_waiting_operations()

    # Check that operations have been marked as success
    with transaction.manager:
        ops = list(dbsession.query(CryptoOperation).all())
        assert len(ops) == 1
        assert isinstance(ops[0], CryptoAddressWithdraw)
        assert ops[0].state == CryptoOperationState.success
        assert ops[0].txid == txid_to_bin(TEST_TXID)
Beispiel #31
0
 def _withdraw_eth(service, dbsession, opid):
     # Mocked withdraw op that always success
     with transaction.manager:
         op = dbsession.query(CryptoOperation).get(opid)
         op.txid = txid_to_bin(TEST_TXID)
         op.mark_complete()
def test_transfer_tokens_between_accounts(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, deposit_address: str, token_asset: str, coinbase: str):
    """Transfer tokens between two internal accounts.

    Do transfers in two batches to make sure subsequent events top up correctly.
    """

    # Initiate a target address creatin
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        opid = CryptoAddress.create_address(network).id

    # Run address creation
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # We resolved address creation operation.
    # Get the fresh address for the future withdraw targets.
    with transaction.manager:
        op = dbsession.query(CryptoAddressCreation).get(opid)
        addr = op.address.address
        assert addr

    # Move tokens between accounts
    with transaction.manager:
        address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        asset = dbsession.query(Asset).get(token_asset)
        caccount = address.get_account(asset)

        op = caccount.withdraw(Decimal(2000), addr, "Sending to friend")
        opid = op.id

    # Resolve the 1st transaction
    eth_service.run_event_cycle()
    wait_for_op_confirmations(eth_service, opid)

    # See that our internal balances match
    with transaction.manager:

        # Withdraw operation is complete
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at, "Op not confirmed {}".format(op)

        # We should have received a Transfer operation targetting target account
        op = dbsession.query(CryptoOperation).join(CryptoAddressAccount).join(CryptoAddress).filter_by(address=addr).one()
        opid = op.id

    # Confirm incoming Transfer
    wait_for_op_confirmations(eth_service, opid, timeout=180)

    # Check Transfer looks valid
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at, "Op not confirmed {}".format(op)

        asset = dbsession.query(Asset).get(token_asset)
        source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        target = dbsession.query(CryptoAddress).filter_by(address=addr).one()

        assert source.get_account(asset).account.get_balance() == 8000
        assert target.get_account(asset).account.get_balance() == 2000

    # Add some ETH on deposit_address
    txid = send_balance_to_address(web3, deposit_address, TEST_VALUE)
    confirm_transaction(web3, txid)
    eth_service.run_event_cycle()

    # Wait for confirmations to have ETH deposit credired
    with transaction.manager:
        op = dbsession.query(CryptoOperation).filter_by(txid=txid_to_bin(txid)).one()
        opid = op.id
        confirmed = op.completed_at
    if not confirmed:
        wait_for_op_confirmations(eth_service, opid)

    # Send some more tokens + ether, so we see account can't get mixed up
    with transaction.manager:
        address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        asset = dbsession.query(Asset).get(token_asset)
        caccount = address.get_account(asset)

        op = caccount.withdraw(Decimal(4000), addr, "Sending tokens to friend äää")

        eth_asset = get_ether_asset(dbsession)
        caccount = address.get_account(eth_asset)
        op = caccount.withdraw(TEST_VALUE, addr, "Sending ETH to friend äää")
        opid = op.id

    # Resolve second transaction
    eth_service.run_event_cycle()
    wait_for_op_confirmations(eth_service, opid)

    # See that our internal balances match
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at
        asset = dbsession.query(Asset).get(token_asset)
        source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        target = dbsession.query(CryptoAddress).filter_by(address=addr).one()
        eth_asset = get_ether_asset(dbsession)

        assert source.get_account(asset).account.get_balance() == 4000
        assert target.get_account(asset).account.get_balance() == 6000
        assert target.get_account(eth_asset).account.get_balance() == TEST_VALUE

    # Transfer 3:
    # Send everything back plus some ether
    with transaction.manager:
        address = dbsession.query(CryptoAddress).filter_by(address=addr).one()
        asset = dbsession.query(Asset).get(token_asset)
        caccount = address.get_account(asset)

        op = caccount.withdraw(Decimal(6000), eth_address_to_bin(deposit_address), "Sending everything back to friend äää")
        opid = op.id

    # Resolve third transaction
    wait_for_op_confirmations(eth_service, opid)
    eth_service.run_event_cycle()

    # See that our internal balances match
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at
        asset = dbsession.query(Asset).get(token_asset)
        source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        target = dbsession.query(CryptoAddress).filter_by(address=addr).one()

        assert source.get_account(asset).account.get_balance() == 10000
        assert target.get_account(asset).account.get_balance() == 0

        eth_asset = get_ether_asset(dbsession)
        assert source.get_account(eth_asset).account.get_balance() == 0
        assert target.get_account(eth_asset).account.get_balance() == TEST_VALUE
def test_transfer_tokens_between_accounts(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, deposit_address: str, token_asset: str, coinbase: str):
    """Transfer tokens between two internal accounts.

    Do transfers in two batches to make sure subsequent events top up correctly.
    """

    # Initiate a target address creatin
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        opid = CryptoAddress.create_address(network).id

    # Run address creation
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # We resolved address creation operation.
    # Get the fresh address for the future withdraw targets.
    with transaction.manager:
        op = dbsession.query(CryptoAddressCreation).get(opid)
        addr = op.address.address
        assert addr

    # Move tokens between accounts
    with transaction.manager:
        address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        asset = dbsession.query(Asset).get(token_asset)
        caccount = address.get_account(asset)

        op = caccount.withdraw(Decimal(2000), addr, "Sending to friend")
        opid = op.id

    # Resolve the 1st transaction
    eth_service.run_event_cycle()
    wait_for_op_confirmations(eth_service, opid)

    # See that our internal balances match
    with transaction.manager:

        # Withdraw operation is complete
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at, "Op not confirmed {}".format(op)

        # We should have received a Transfer operation targetting target account
        op = dbsession.query(CryptoOperation).join(CryptoAddressAccount).join(CryptoAddress).filter_by(address=addr).one()
        opid = op.id

    # Confirm incoming Transfer
    wait_for_op_confirmations(eth_service, opid, timeout=180)

    # Check Transfer looks valid
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at, "Op not confirmed {}".format(op)

        asset = dbsession.query(Asset).get(token_asset)
        source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        target = dbsession.query(CryptoAddress).filter_by(address=addr).one()

        assert source.get_account(asset).account.get_balance() == 8000
        assert target.get_account(asset).account.get_balance() == 2000

    # Add some ETH on deposit_address
    txid = send_balance_to_address(web3, deposit_address, TEST_VALUE)
    confirm_transaction(web3, txid)
    eth_service.run_event_cycle()

    # Wait for confirmations to have ETH deposit credired
    with transaction.manager:
        op = dbsession.query(CryptoOperation).filter_by(txid=txid_to_bin(txid)).one()
        opid = op.id
        confirmed = op.completed_at
    if not confirmed:
        wait_for_op_confirmations(eth_service, opid)

    # Send some more tokens + ether, so we see account can't get mixed up
    with transaction.manager:
        address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        asset = dbsession.query(Asset).get(token_asset)
        caccount = address.get_account(asset)

        op = caccount.withdraw(Decimal(4000), addr, "Sending tokens to friend äää")

        eth_asset = get_ether_asset(dbsession)
        caccount = address.get_account(eth_asset)
        op = caccount.withdraw(TEST_VALUE, addr, "Sending ETH to friend äää")
        opid = op.id

    # Resolve second transaction
    eth_service.run_event_cycle()
    wait_for_op_confirmations(eth_service, opid)

    # See that our internal balances match
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at
        asset = dbsession.query(Asset).get(token_asset)
        source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        target = dbsession.query(CryptoAddress).filter_by(address=addr).one()
        eth_asset = get_ether_asset(dbsession)

        assert source.get_account(asset).account.get_balance() == 4000
        assert target.get_account(asset).account.get_balance() == 6000
        assert target.get_account(eth_asset).account.get_balance() == TEST_VALUE

    # Transfer 3:
    # Send everything back plus some ether
    with transaction.manager:
        address = dbsession.query(CryptoAddress).filter_by(address=addr).one()
        asset = dbsession.query(Asset).get(token_asset)
        caccount = address.get_account(asset)

        op = caccount.withdraw(Decimal(6000), eth_address_to_bin(deposit_address), "Sending everything back to friend äää")
        opid = op.id

    # Resolve third transaction
    wait_for_op_confirmations(eth_service, opid)
    eth_service.run_event_cycle()

    # See that our internal balances match
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at
        assert op.completed_at
        asset = dbsession.query(Asset).get(token_asset)
        source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one()
        target = dbsession.query(CryptoAddress).filter_by(address=addr).one()

        assert source.get_account(asset).account.get_balance() == 10000
        assert target.get_account(asset).account.get_balance() == 0

        eth_asset = get_ether_asset(dbsession)
        assert source.get_account(eth_asset).account.get_balance() == 0
        assert target.get_account(eth_asset).account.get_balance() == TEST_VALUE