Reject withdrawal addresses that aren't PLM
embit's Script.from_address accepts a well-formed bech32 address from any chain: a Bitcoin bc1... parses into a perfectly valid witness program. So a withdrawal to a BTC address built, signed and broadcast normally on PLM, and the funds landed on a script nobody holds the key for — silently, with no error anywhere. A malformed address fared slightly better only in that it crashed the request with an unhandled 500. is_valid_plm_address checks the HRP as well as the parse, and runs first in request_withdrawal, before a single UTXO is touched. It matches what the withdrawal form already told the user (bech32 plm1q... only). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -97,3 +97,28 @@ async def test_withdrawal_rejects_insufficient_balance(session_factory):
|
||||
user = await session.get(User, user_id)
|
||||
with pytest.raises(WithdrawalError, match="insufficient balance"):
|
||||
await request_withdrawal(session, client, user, EXTERNAL_ADDRESS, BET_AMOUNT_SATS)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"address",
|
||||
[
|
||||
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", # valid bech32, wrong chain
|
||||
"plm1qbogus", # right HRP, broken checksum
|
||||
"not-an-address",
|
||||
],
|
||||
)
|
||||
async def test_withdrawal_rejects_non_plm_address(session_factory, address):
|
||||
"""The bc1 case is the one that matters: embit parses it into a perfectly
|
||||
valid witness program, so without the HRP check the withdrawal would build,
|
||||
sign and broadcast on PLM, sending the funds somewhere nobody holds a key
|
||||
for. It has to fail before a single UTXO is touched."""
|
||||
user_id = await _make_funded_user(session_factory, 3, 2_000_000_000)
|
||||
client = FakeElectrumClient()
|
||||
|
||||
async with session_factory() as session:
|
||||
user = await session.get(User, user_id)
|
||||
with pytest.raises(WithdrawalError) as exc_info:
|
||||
await request_withdrawal(session, client, user, address, BET_AMOUNT_SATS)
|
||||
|
||||
assert exc_info.value.code == "invalid_address"
|
||||
assert not client.broadcasted
|
||||
|
||||
Reference in New Issue
Block a user