Ejemplo n.º 1
0
def test_double_address(dbsession, eth_network_id):
    """Cannot create Address object under the same network twice."""

    with pytest.raises(sqlalchemy.exc.IntegrityError):
        with transaction.manager:
            network = dbsession.query(AssetNetwork).get(eth_network_id)
            address = CryptoAddress(network=network,
                                    address=eth_address_to_bin(TEST_ADDRESS))
            dbsession.add(address)
            dbsession.flush()
            address = CryptoAddress(network=network,
                                    address=eth_address_to_bin(TEST_ADDRESS))
            dbsession.add(address)
def test_create_eth_account(dbsession, eth_service, eth_network_id):
    """Create Ethereum user deposit address by deploying a hosted wallet contract through geth."""

    # Create CryptoAddressCreation operation
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)

        address = CryptoAddress(network=network)

        dbsession.flush()

        # Generate address on the account
        op = CryptoAddressCreation(address)
        dbsession.add(op)
        dbsession.flush()

        op_id = op.id

    # Event cycle should perform the operation
    success_op_count, failed_op_count = eth_service.run_event_cycle()
    assert success_op_count == 1
    assert failed_op_count == 0

    with transaction.manager:
        assert dbsession.query(CryptoOperation).count() == 1
        op = dbsession.query(CryptoOperation).get(op_id)
        assert op.completed_at
        assert op.block
        assert op.performed_at
        assert op.completed_at

        address = dbsession.query(CryptoAddress).first()
        assert address.address
Ejemplo n.º 3
0
def deposit_address(eth_service, eth_network_id, dbsession, registry) -> str:
    """Creates an address that has matching account on Geth.

    Sending ETH to this address should trigger a incoming tx logic.

    :return: 0x hex presentation
    """

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)

        address = CryptoAddress(network=network)

        dbsession.flush()

        # Generate address on the account
        op = CryptoAddressCreation(address)
        dbsession.add(op)
        dbsession.flush()

    # Creates a hosted wallet
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1

    with transaction.manager:
        return bin_to_eth_address(dbsession.query(CryptoAddress).one().address)
Ejemplo n.º 4
0
def test_account_creation_completed(dbsession, eth_network_id,
                                    eth_service: MockEthereumService,
                                    captured_registry_events):
    """We receive completed event on account creation."""

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)

        address = CryptoAddress(network=network)

        dbsession.flush()

        # Generate address on the account
        op = CryptoAddressCreation(address)
        op.required_confirmation_count = 1
        dbsession.add(op)
        dbsession.flush()

        op_id = op.id

    eth_service.run_test_ops()

    # We get performed event on
    events = captured_registry_events
    assert len(events) == 2
    assert isinstance(events[0], CryptoOperationPerformed)
    assert isinstance(events[1], CryptoOperationCompleted)
Ejemplo n.º 5
0
def test_create_address_accounts(dbsession, eth_network_id, eth_service,
                                 eth_faux_address, eth_asset_id):
    """Check that we can cerate different accounts under an account."""

    # Create ETH account
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        asset = dbsession.query(Asset).get(eth_asset_id)
        dbsession.flush()
        account = address.get_or_create_account(asset)
        account_id = account.id

    # We get it reflected back second time
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_or_create_account(asset)
        assert account.id == account_id

    # We cannot create double account for the same asset
    with pytest.raises(MultipleAssetAccountsPerAddress):
        with transaction.manager:
            address = dbsession.query(CryptoAddress).one()
            asset = dbsession.query(Asset).get(eth_asset_id)
            address.create_account(asset)
Ejemplo n.º 6
0
def test_create_eth_account(dbsession, eth_network_id, eth_service):
    """Create Ethereum account on Ethereum node."""

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)

        address = CryptoAddress(network=network)

        dbsession.flush()

        # Generate address on the account
        op = CryptoAddressCreation(address)
        dbsession.add(op)
        dbsession.flush()

        op_id = op.id

    success_op_count, failed_op_count = mock_create_addresses(
        eth_service, dbsession)

    assert success_op_count == 1
    assert failed_op_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(op_id)
        assert op.completed_at

        address = dbsession.query(CryptoAddress).first()
        assert address.address
Ejemplo n.º 7
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
Ejemplo n.º 8
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_import_token(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, coinbase: str, deposit_address: str, token: Token):
    """Import an existing smart contract token to system."""

    # Make sure we have an address that holds some of the tokens so it is cleared up during import
    txid = token.transfer(deposit_address, Decimal(4000))
    confirm_transaction(web3, txid)

    with transaction.manager:

        network = dbsession.query(AssetNetwork).get(eth_network_id)
        op = import_token(network, eth_address_to_bin(token.address))
        opid = op.id

        # Let's create another address that doesn't hold tokens
        # and see that import doesn't fail for it
        addr = CryptoAddress(network=network, address=eth_address_to_bin("0x2f70d3d26829e412a602e83fe8eebf80255aeea5"))
        dbsession.add(addr)

    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # Check that we created a new asset and its imports where fulfilled
    with transaction.manager:

        op = dbsession.query(CryptoOperation).get(opid)
        assert op.completed_at

        # We got account with tokens on it
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        caddress = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))

        asset = network.assets.filter_by(external_id=eth_address_to_bin(token.address)).one()
        assert asset.name == "Mootoken"
        assert asset.symbol == "MOO"
        assert asset.supply == 10000

        caccount = caddress.get_account_by_address(eth_address_to_bin(token.address))
        assert caccount.account.get_balance() == 4000
Ejemplo n.º 10
0
def eth_faux_address(dbsession, registry, eth_network_id):
    """Create a faux address that is not registered on node."""
    with transaction.manager:
        address = CryptoAddress(network_id=eth_network_id)
        address.address = "xxx"
    return address.address
Ejemplo n.º 11
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)