Files
neuron-tai/tests/test_devnet_treasury.py
Dobromir Popov 7cf8d9bcf3 test descriptions
2026-07-11 22:25:30 +03:00

244 lines
8.6 KiB
Python

"""US-032: devnet custodial treasury — wallet binding + deposit watcher.
The deposit watcher polls the treasury token account and credits the sending
wallet's bound API key exactly once per transaction signature. The watcher is
exercised against a fake treasury (hermetic CI, ADR-0007); the real
solana-test-validator flow runs only when the binary is installed.
"""
import json
import shutil
import time
import urllib.error
import urllib.request
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from meshnet_tracker.billing import BillingLedger
from meshnet_tracker.server import TrackerServer
from meshnet_tracker.wallet_proof import binding_message
def _keypair():
"""Deterministic-enough local ed25519 keypair + base58 address for tests."""
priv = Ed25519PrivateKey.generate()
from meshnet_node.wallet import _b58encode
public = priv.public_key().public_bytes_raw()
return priv, _b58encode(public)
def _sign(priv: Ed25519PrivateKey, api_key: str, wallet: str) -> str:
return priv.sign(binding_message(api_key, wallet)).hex()
class _FakeDeposit:
def __init__(self, signature, sender_wallet, amount_usdt):
self.signature = signature
self.sender_wallet = sender_wallet
self.amount_usdt = amount_usdt
class _FakeTreasury:
"""Stands in for SolanaCustodialTreasury: a queue of confirmed deposits."""
def __init__(self):
self.deposits: list[_FakeDeposit] = []
def list_new_deposits(self, is_seen, *, limit=200):
return [d for d in self.deposits if not is_seen(d.signature)]
def _post_json(url: str, payload: dict, headers: dict | None = None) -> dict:
req = urllib.request.Request(
url,
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json", **(headers or {})},
method="POST",
)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
@pytest.fixture
def watched_tracker():
ledger = BillingLedger(starting_credit=0.0, default_price_per_1k=0.02)
treasury = _FakeTreasury()
tracker = TrackerServer(
billing=ledger,
treasury=treasury,
deposit_poll_interval=0.1,
)
port = tracker.start()
yield f"http://127.0.0.1:{port}", ledger, treasury
tracker.stop()
def _wait_for(predicate, timeout=3.0):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
if predicate():
return True
time.sleep(0.05)
return False
def test_wallet_register_requires_auth(watched_tracker):
"Wallet register requires auth\n\nTags: auth, security, wallet"
tracker_url, _, _ = watched_tracker
with pytest.raises(urllib.error.HTTPError) as exc_info:
_post_json(f"{tracker_url}/v1/wallet/register", {"wallet": "So1anaWa11et111"})
assert exc_info.value.code == 401
def test_deposit_credits_bound_api_key_exactly_once(watched_tracker):
"Deposit credits bound api key exactly once\n\nTags: general"
tracker_url, ledger, treasury = watched_tracker
priv, wallet = _keypair()
reply = _post_json(
f"{tracker_url}/v1/wallet/register",
{"wallet": wallet, "signature": _sign(priv, "client-key-1", wallet)},
headers={"Authorization": "Bearer client-key-1"},
)
assert reply["bound"] is True
treasury.deposits.append(_FakeDeposit("sig-1", wallet, 25.0))
assert _wait_for(lambda: ledger.get_client_balance("client-key-1") > 0)
assert ledger.get_client_balance("client-key-1") == pytest.approx(25.0)
# the same signature keeps being reported by the RPC — credited once only
time.sleep(0.4)
assert ledger.get_client_balance("client-key-1") == pytest.approx(25.0)
# replay through the ledger API directly is also a no-op
assert ledger.credit_deposit("client-key-1", 25.0, "sig-1") is None
def test_unbound_wallet_deposit_is_not_credited(watched_tracker):
"Unbound wallet deposit is not credited\n\nTags: security, wallet"
_, ledger, treasury = watched_tracker
treasury.deposits.append(_FakeDeposit("sig-2", "UnknownWallet999", 10.0))
time.sleep(0.4)
assert ledger.snapshot()["clients"] == {}
def test_binding_replicates_via_events():
"Binding replicates via events\n\nTags: general"
a = BillingLedger(starting_credit=0.0)
b = BillingLedger(starting_credit=0.0)
a.bind_wallet("key-9", "WalletNine")
a.credit_deposit("key-9", 3.0, "sig-9")
events, _ = a.events_since(0)
b.apply_events(events)
assert b.api_key_for_wallet("WalletNine") == "key-9"
assert b.get_client_balance("key-9") == pytest.approx(3.0)
# deposit event id embeds the signature → replicated replay is a no-op
assert b.apply_events(events) == 0
def test_solana_adapter_derives_treasury_accounts():
"Adapter smoke test without any RPC round-trip.\n\nTags: general"
pytest.importorskip("solders")
token_instructions = pytest.importorskip("spl.token.instructions")
if not hasattr(token_instructions, "InitializeMintParams"):
pytest.skip("installed spl.token lacks InitializeMintParams")
from solders.keypair import Keypair
from meshnet_contracts.solana_adapter import SolanaCustodialTreasury
keypair = Keypair()
treasury = SolanaCustodialTreasury(
"http://127.0.0.1:8899",
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", # real USDT mint address shape
keypair,
)
assert treasury.treasury_wallet == str(keypair.pubkey())
assert treasury.treasury_token_account # deterministic ATA derivation
@pytest.mark.skipif(
shutil.which("solana-test-validator") is None,
reason="solana-test-validator not installed",
)
def test_mint_deposit_credit_flow_against_local_validator(tmp_path):
"Full mint → deposit → credit flow on a local validator (US-032).\n\nTags: general"
import subprocess
from solders.keypair import Keypair
from meshnet_contracts.solana_adapter import RpcClient, SolanaCustodialTreasury
rpc_url = "http://127.0.0.1:8899"
ledger_dir = tmp_path / "ledger"
validator = subprocess.Popen(
["solana-test-validator", "--ledger", str(ledger_dir), "--quiet",
"--rpc-port", "8899", "--reset"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
try:
assert _wait_for(lambda: _rpc_alive(RpcClient(rpc_url)), timeout=60)
treasury_kp, client_kp = Keypair(), Keypair()
bootstrap = SolanaCustodialTreasury(
rpc_url, "11111111111111111111111111111111", treasury_kp,
)
client_bootstrap = SolanaCustodialTreasury(
rpc_url, "11111111111111111111111111111111", client_kp,
)
bootstrap.request_airdrop(2.0)
client_bootstrap.request_airdrop(2.0)
assert _wait_for(lambda: bootstrap.get_sol_balance() > 0, timeout=30)
assert _wait_for(lambda: client_bootstrap.get_sol_balance() > 0, timeout=30)
treasury, mint = bootstrap.create_mock_usdt_mint()
assert _wait_for(
lambda: treasury._account_exists( # noqa: SLF001 — test-only poke
__import__("solders.pubkey", fromlist=["Pubkey"]).Pubkey.from_string(mint)
),
timeout=30,
)
treasury.ensure_token_account(treasury.treasury_wallet)
treasury.mint_mock_usdt(str(client_kp.pubkey()), 50.0)
# the client depositing into the treasury is just a transfer the
# client signs — reuse send_payouts from an adapter bound to their key
client_side = SolanaCustodialTreasury(rpc_url, mint, client_kp)
assert _wait_for(
lambda: _try_send(client_side, treasury.treasury_wallet, 25.0), timeout=30,
)
ledger = BillingLedger(starting_credit=0.0)
ledger.bind_wallet("api-key-e2e", str(client_kp.pubkey()))
def _credited():
for dep in treasury.list_new_deposits(
lambda sig: ledger.has_event(f"deposit-{sig}")
):
key = ledger.api_key_for_wallet(dep.sender_wallet)
if key:
ledger.credit_deposit(key, dep.amount_usdt, dep.signature)
return ledger.get_client_balance("api-key-e2e") > 0
assert _wait_for(_credited, timeout=60)
assert ledger.get_client_balance("api-key-e2e") == pytest.approx(25.0)
finally:
validator.terminate()
validator.wait(timeout=10)
def _try_send(adapter, wallet: str, amount: float) -> bool:
try:
adapter.send_payouts([(wallet, amount)])
return True
except Exception:
return False
def _rpc_alive(rpc) -> bool:
try:
return rpc.call("getHealth") == "ok"
except Exception:
return False