Files
plm-lottery/tests/unit/test_payout_builder.py
T

101 lines
3.8 KiB
Python
Raw Normal View History

2026-07-21 10:25:29 +02:00
import pytest
from embit import script
from embit.bip32 import HDKey
from embit.transaction import Transaction
from app.wallet.plm_network import PLM_MAINNET
from app.wallet.psbt_builder import InsufficientFundsError, Utxo, build_payout_transaction, estimate_vsize
def _key(seed_byte: int) -> HDKey:
root = HDKey.from_seed(bytes([seed_byte]) * 32, version=PLM_MAINNET["xprv"])
return root.derive("m/84h/746h/0h/0/0")
def test_payout_deducts_fee_only_from_winner_share():
pool_key = _key(10)
pool_script = script.p2wpkh(pool_key.to_public())
pool_address = pool_script.address(network=PLM_MAINNET)
winner_address = script.p2wpkh(_key(11).to_public()).address(network=PLM_MAINNET)
fee_address = script.p2wpkh(_key(12).to_public()).address(network=PLM_MAINNET)
pool_amount = 10_000_000_000 # 100 PLM pot
winner_share = pool_amount * 70 // 100
commission = pool_amount - winner_share
utxos = [Utxo("aa" * 32, 0, pool_amount)]
built = build_payout_transaction(
signing_key=pool_key,
from_script=pool_script,
utxos=utxos,
winner_address=winner_address,
winner_share_sats=winner_share,
fee_address=fee_address,
commission_sats=commission,
change_address=pool_address,
fee_rate_sat_vb=1,
)
fee = estimate_vsize(1, 3)
assert built.fee_sats == fee
assert built.winner_sats == winner_share - fee
assert built.commission_sats == commission # untouched by the fee
assert built.change_sats == pool_amount - (winner_share + commission)
parsed = Transaction.parse(bytes.fromhex(built.raw_hex))
assert len(parsed.vout) == 2 # no change needed: winner_share + commission == pool_amount exactly
amounts = sorted(o.value for o in parsed.vout)
assert amounts == sorted([built.winner_sats, built.commission_sats])
def test_payout_adds_change_output_when_pool_utxos_exceed_target():
pool_key = _key(20)
pool_script = script.p2wpkh(pool_key.to_public())
pool_address = pool_script.address(network=PLM_MAINNET)
winner_address = script.p2wpkh(_key(21).to_public()).address(network=PLM_MAINNET)
fee_address = script.p2wpkh(_key(22).to_public()).address(network=PLM_MAINNET)
winner_share = 700_000_000
commission = 300_000_000
utxos = [Utxo("bb" * 32, 0, 2_000_000_000)] # more than winner_share+commission
built = build_payout_transaction(
signing_key=pool_key,
from_script=pool_script,
utxos=utxos,
winner_address=winner_address,
winner_share_sats=winner_share,
fee_address=fee_address,
commission_sats=commission,
change_address=pool_address,
fee_rate_sat_vb=1,
)
assert built.change_sats == 2_000_000_000 - (winner_share + commission)
parsed = Transaction.parse(bytes.fromhex(built.raw_hex))
assert len(parsed.vout) == 3
def test_payout_raises_when_winner_share_too_small():
pool_key = _key(30)
pool_script = script.p2wpkh(pool_key.to_public())
pool_address = pool_script.address(network=PLM_MAINNET)
winner_address = script.p2wpkh(_key(31).to_public()).address(network=PLM_MAINNET)
fee_address = script.p2wpkh(_key(32).to_public()).address(network=PLM_MAINNET)
winner_share = 100 # smaller than the ~172 sat fee at 1 sat/vB for 1-in-3-out
commission = 50
assert winner_share < estimate_vsize(1, 3)
utxos = [Utxo("cc" * 32, 0, winner_share + commission)]
with pytest.raises(InsufficientFundsError):
build_payout_transaction(
signing_key=pool_key,
from_script=pool_script,
utxos=utxos,
winner_address=winner_address,
winner_share_sats=winner_share,
fee_address=fee_address,
commission_sats=commission,
change_address=pool_address,
fee_rate_sat_vb=1,
)