Give every round its own pool address (B-80, RND-35, DAT-60, DAT-11, REG-14, SEC-08, BET-12, RND-22, WDR-14, SEC-43, API-23, API-36)
All bets of all rounds paid one address (m/84'/746'/0'/1/0), so its history grew forever, other rounds' money and stray coins mixed with a round's own, and the payout picked from everything it held. - hd.py: derive_pool_key/derive_pool_address(round_id) on branch 2 (m/84'/746'/0'/2/<round_id>); no argument is the reserve address 1/0. derive_round_pool_key checks the key against the address stored on the round, so a payout can never be signed for money its key does not control. - rounds.pool_address (migration 477bddae1a84, existing rounds backfilled with the reserve address since that is where their bets went; add_column, not a batch alter, so the expression indexes stay): written in the transaction that inserts the round, once its id exists. - place_bet pays round.pool_address; the payout reads, signs and takes change at the round's own address; own_address_for and bump_fee's _signing_context follow the round (confirmation poller and reconciler pass round_id). - Withdrawals to any round pool address, the reserve address or fee_address are refused (withdrawal_to_platform_address, 400, all seven languages). - GET /rounds/current and GET /admin/rounds expose pool_address. - RND-22/DAT-11 stay "da implementare" for B-82: the payout still spends every confirmed coin at the round's address, not only the participants' outpoints (participants carry no bet_vout yet). - tests/unit/conftest.py: every test now gets a throwaway master key. Opening a round derives an address, and tests that merely opened a round were silently reading the real key from the local .env. - Existing tests changed because the spec changed: payout fixtures give their round a pool_address; the reconciler's address-cache test uses two payouts of one round (rows of different rounds no longer share an address); own_address_for now takes the round id. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cc63753439
commit
fd6a91e93d
37 files changed
+673
-144
No files matched your search
@@ -241,6 +241,7 @@ class AdminRoundResponse(BaseModel):
|
||||
winner_amount_sats: int | None
|
||||
fee_amount_sats: int | None
|
||||
payout_txid: str | None
|
||||
pool_address: str | None # B-80
|
||||
|
||||
|
||||
@router.get("/rounds", response_model=list[AdminRoundResponse], dependencies=[Depends(require_admin)])
|
||||
@@ -268,6 +269,7 @@ async def list_rounds(
|
||||
winner_amount_sats=r.winner_amount_sats,
|
||||
fee_amount_sats=r.fee_amount_sats,
|
||||
payout_txid=r.payout_txid,
|
||||
pool_address=r.pool_address or None,
|
||||
)
|
||||
for r in rounds
|
||||
]
|
||||
|
||||
@@ -89,6 +89,9 @@ class CurrentRoundResponse(BaseModel):
|
||||
server_time: str
|
||||
round_id: int | None = None
|
||||
status: str | None = None
|
||||
# B-80 (RND-35): this round's own pool address, so anyone can verify its money on
|
||||
# an explorer; null when there is no active round.
|
||||
pool_address: str | None = None
|
||||
opened_at: str | None = None
|
||||
closes_at: str | None = None
|
||||
# B-65: confirmed participants only — the ones the draw actually picks from and
|
||||
@@ -205,6 +208,7 @@ async def current_round(
|
||||
server_time=datetime.now(timezone.utc).isoformat(),
|
||||
round_id=round_.id,
|
||||
status=round_.status,
|
||||
pool_address=round_.pool_address or None,
|
||||
opened_at=opened_at.isoformat(),
|
||||
closes_at=closes_at.isoformat(),
|
||||
participant_count=participant_count,
|
||||
|
||||
Reference in new issue
Block a user