"""US-006 tests: Solana contract boundary for stake, payment, and settlement.""" from meshnet_contracts import LocalSolanaContracts def test_node_stake_is_reflected_in_registry(): "A node can submit stake and read the updated registry balance.\n\nTags: billing, contracts, settlement" contracts = LocalSolanaContracts() receipt = contracts.registry.submit_stake("node-wallet-a", 500) assert receipt["cluster"] == "local-test-validator" assert contracts.registry.get_wallet("node-wallet-a").stake_balance == 500 assert contracts.registry.has_minimum_stake("node-wallet-a", minimum_stake=100) def test_client_can_fund_api_key_with_testnet_sol(): "A client can fund an API key account and read the available balance.\n\nTags: billing, contracts, settlement" contracts = LocalSolanaContracts() receipt = contracts.payment.fund_api_key("api-key-a", lamports=2_000) assert receipt["cluster"] == "local-test-validator" assert contracts.payment.get_balance("api-key-a").lamports == 3_000 def test_epoch_settlement_debits_clients_and_rewards_nodes_and_validator(): "Settlement distributes native token rewards according to recorded work.\n\nTags: billing, contracts, settlement" contracts = LocalSolanaContracts( cost_per_layer_token_lamport=2, probationary_job_count=0, ) contracts.payment.fund_api_key("api-key-a", lamports=1_000) contracts.payment.record_attribution( session_id="session-a", api_key="api-key-a", node_wallet="node-wallet-a", layer_start=0, layer_end=15, tokens=10, ) contracts.payment.record_attribution( session_id="session-a", api_key="api-key-a", node_wallet="node-wallet-b", layer_start=16, layer_end=31, tokens=10, speed_score=2.0, ) result = contracts.settlement.settle_epoch( epoch=1, validator_wallet="validator-wallet", validator_reward_share_bps=1_000, ) assert result.client_debits == {"api-key-a": 640} assert result.node_rewards == {"node-wallet-a": 96, "node-wallet-b": 192} assert result.validator_rewards == {"validator-wallet": 32} assert contracts.payment.get_balance("api-key-a").lamports == 1_360 assert contracts.settlement.get_token_balance("node-wallet-a") == 96 assert contracts.settlement.get_token_balance("node-wallet-b") == 192 assert contracts.settlement.get_token_balance("validator-wallet") == 32 def test_failed_settlement_does_not_advance_probation_or_settle_attribution(): "Settlement state is unchanged when caller balance validation fails.\n\nTags: billing, contracts, settlement" contracts = LocalSolanaContracts(starting_credit_lamports=0, probationary_job_count=1) contracts.payment.record_attribution( session_id="session-a", api_key="api-key-a", node_wallet="node-wallet-a", layer_start=0, layer_end=0, tokens=1, ) try: contracts.settlement.settle_epoch(epoch=1) raise AssertionError("Expected insufficient balance") except ValueError as exc: assert "insufficient SOL balance" in str(exc) assert contracts.registry.get_wallet("node-wallet-a").completed_job_count == 0 assert contracts.payment.list_attributions(include_settled=False) def test_slash_proof_reduces_stake_increments_strikes_and_warns(capsys): "Submitting a slash proof updates registry state and notifies the operator.\n\nTags: billing, contracts, settlement" contracts = LocalSolanaContracts() contracts.registry.submit_stake("node-wallet-a", 500) receipt = contracts.registry.submit_slash_proof( wallet_address="node-wallet-a", slash_amount=125, strike_threshold=2, reason="test divergence", ) wallet = contracts.registry.get_wallet("node-wallet-a") assert receipt.signature.startswith("local-slash-node-wallet-a") assert wallet.stake_balance == 375 assert wallet.strike_count == 1 assert wallet.banned is False assert "WARNING: node node-wallet-a slashed" in capsys.readouterr().out def test_probationary_wallet_earns_only_after_required_jobs(): "A new wallet's first N completed jobs are counted but not rewarded.\n\nTags: billing, contracts, security, settlement, wallet" contracts = LocalSolanaContracts(probationary_job_count=2) contracts.payment.fund_api_key("api-key-a", lamports=1_000) for job_number in range(3): contracts.payment.record_attribution( session_id=f"session-{job_number}", api_key="api-key-a", node_wallet="new-wallet", layer_start=0, layer_end=0, tokens=10, ) result = contracts.settlement.settle_epoch(epoch=1) assert result.client_debits == {"api-key-a": 30} assert result.node_rewards == {"new-wallet": 10} assert contracts.registry.get_wallet("new-wallet").completed_job_count == 3 assert contracts.registry.probationary_jobs_remaining("new-wallet") == 0 assert contracts.settlement.get_token_balance("new-wallet") == 10 def test_probationary_wallet_receives_no_rewards_before_threshold(): "Settlement state verifies a wallet earns zero during probation.\n\nTags: billing, contracts, security, settlement, wallet" contracts = LocalSolanaContracts(probationary_job_count=50) contracts.payment.fund_api_key("api-key-a", lamports=1_000) contracts.payment.record_attribution( session_id="session-a", api_key="api-key-a", node_wallet="new-wallet", layer_start=0, layer_end=9, tokens=1, ) result = contracts.settlement.settle_epoch(epoch=1) assert result.node_rewards == {"new-wallet": 0} assert contracts.registry.get_wallet("new-wallet").completed_job_count == 1 assert contracts.registry.probationary_jobs_remaining("new-wallet") == 49 assert contracts.settlement.get_token_balance("new-wallet") == 0 def test_slash_proof_at_threshold_marks_wallet_banned(): "Registry marks a wallet banned when strikes reach the configured threshold.\n\nTags: billing, contracts, security, settlement, wallet" contracts = LocalSolanaContracts() contracts.registry.submit_stake("node-wallet-a", 500) contracts.registry.submit_slash_proof( wallet_address="node-wallet-a", slash_amount=10, strike_threshold=2, ) receipt = contracts.registry.submit_slash_proof( wallet_address="node-wallet-a", slash_amount=10, strike_threshold=2, ) assert receipt.banned is True wallet = contracts.registry.get_wallet("node-wallet-a") assert wallet.strike_count == 2 assert wallet.banned is True