"""B-41: own_address_for is the single place tx/confirmation.py and tx/reconcile.py derive a PendingTransaction's own address from — a payout's address must always be the pool's, everything else the actual user's.""" import pytest from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from app.config import settings from app.db.base import Base from app.db.models import User from app.tx.pending_address import own_address_for @pytest.fixture async def session_factory(tmp_path, monkeypatch): monkeypatch.setattr(settings, "master_key_path", str(tmp_path / "master.xprv.enc")) monkeypatch.setattr( settings, "xprv_encryption_key", __import__("cryptography.fernet", fromlist=["Fernet"]).Fernet.generate_key().decode(), ) from app.wallet import hd hd._account_key = None hd.generate_master_key() engine = create_async_engine("sqlite+aiosqlite:///:memory:") async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) yield async_sessionmaker(engine, expire_on_commit=False) await engine.dispose() hd._account_key = None async def test_payout_uses_the_pool_address_stored_on_its_round(session_factory): """B-80 (DAT-60): the address comes from the round row, whatever the user_id.""" from app.db.models import Round from app.wallet.hd import derive_pool_address async with session_factory() as session: session.add(Round(id=7, status="paying_out", pool_address=derive_pool_address(7))) session.add(Round(id=8, status="closed", pool_address=derive_pool_address(8))) await session.flush() address_7 = await own_address_for(session, "payout", None, 7) address_8 = await own_address_for(session, "payout", None, 8) assert address_7 == derive_pool_address(7) assert address_8 == derive_pool_address(8) assert address_7 != address_8 async def test_payout_of_a_round_without_a_pool_address_is_an_error(session_factory): from app.db.models import Round async with session_factory() as session: session.add(Round(id=9, status="closed")) # pool_address stays "" await session.flush() with pytest.raises(ValueError): await own_address_for(session, "payout", None, 9) with pytest.raises(ValueError): await own_address_for(session, "payout", None, None) @pytest.mark.parametrize("kind", ["bet", "withdrawal"]) async def test_bet_and_withdrawal_use_the_users_own_address(session_factory, kind): from app.wallet.hd import derive_user_address async with session_factory() as session: user = User(username="alice", password_hash="x", derivation_index=3, address=derive_user_address(3)) session.add(user) await session.flush() address = await own_address_for(session, kind, user.id) assert address == derive_user_address(3)