diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 2ec809341..3242edc1a 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -1,3 +1,10 @@ +## BoTTube Premium API + +- GET https://bottube.ai/api/premium/videos +- GET https://bottube.ai/api/premium/analytics/ + +--- +
# 🧱 RustChain: ε€θ‘£θ―ζ˜ŽεŒΊε—ι“Ύ diff --git a/miners/checksums.sha256 b/miners/checksums.sha256 index 0412ea8be..69cf98f36 100644 --- a/miners/checksums.sha256 +++ b/miners/checksums.sha256 @@ -1,4 +1,4 @@ -4afd5aea552cc5b68364b39fa37cdc93d1d406ec295670969e1a9c4164babb15 linux/rustchain_linux_miner.py +c7af612bb2630d5fe6576bb132bdeb7a00ba0be042ec168887ab767a1f16c9f9 linux/rustchain_linux_miner.py cdfca6e63ecd24f53b30140dd44df42415a3254c68aad95b1fca3c1557e15f7b linux/fingerprint_checks.py 603d9a3b3ebfe1a0ca56a60988db4b5d4a80ab57cb5feb1c0b563a1d4020fcd7 macos/rustchain_mac_miner_v2.4.py 163fafcf751d8fbd41bf936facaeb366c042f467fa34b79f2c4c0a45472ef70f macos/rustchain_mac_miner_v2.5.py diff --git a/miners/gpu_fingerprint.py b/miners/gpu_fingerprint.py index 133ad81ae..0f81f06b2 100644 --- a/miners/gpu_fingerprint.py +++ b/miners/gpu_fingerprint.py @@ -24,6 +24,8 @@ Author: Elyan Labs (RIP-0308: Proof of Physical AI) """ +from __future__ import annotations + import argparse import json import hashlib @@ -39,13 +41,17 @@ try: import torch import torch.cuda + HAS_TORCH = True except ImportError: - print("ERROR: PyTorch with CUDA support required. Install: pip install torch") - sys.exit(1) + HAS_TORCH = False -if not torch.cuda.is_available(): - print("ERROR: No CUDA-capable GPU detected.") - sys.exit(1) +def check_requirements(): + if not HAS_TORCH: + print("ERROR: PyTorch with CUDA support required. Install: pip install torch") + sys.exit(1) + if not torch.cuda.is_available(): + print("ERROR: No CUDA-capable GPU detected.") + sys.exit(1) # --------------------------------------------------------------------------- @@ -789,6 +795,7 @@ def cross_validate_gpu(device: torch.device) -> ChannelResult: def run_gpu_fingerprint(device_index: int = 0, samples: int = 200, epoch_salt: str = "") -> GPUFingerprint: """Run all GPU fingerprint channels and return results.""" + check_requirements() device = torch.device(f"cuda:{device_index}") # GPU info diff --git a/otc-bridge/otc_bridge.py b/otc-bridge/otc_bridge.py index bce40c5aa..83d40df6d 100644 --- a/otc-bridge/otc_bridge.py +++ b/otc-bridge/otc_bridge.py @@ -262,7 +262,9 @@ def rtc_transfer_from_worker(recipient_wallet, amount_rtc, order_id): try: last_payload = transfer_r.json() - except ValueError: + if not isinstance(last_payload, dict): + last_payload = {} + except Exception: last_payload = {} if transfer_r.ok: diff --git a/setup_miner.py b/setup_miner.py index 2468806f2..ada898628 100644 --- a/setup_miner.py +++ b/setup_miner.py @@ -20,7 +20,7 @@ MINER_ARTIFACTS = { "Linux": { "url": "https://raw.githubusercontent.com/Scottcjn/Rustchain/main/miners/linux/rustchain_linux_miner.py", - "sha256": "4afd5aea552cc5b68364b39fa37cdc93d1d406ec295670969e1a9c4164babb15", + "sha256": "c7af612bb2630d5fe6576bb132bdeb7a00ba0be042ec168887ab767a1f16c9f9", }, "Darwin": { "url": "https://raw.githubusercontent.com/Scottcjn/Rustchain/main/miners/macos/rustchain_mac_miner_v2.5.py", @@ -28,7 +28,7 @@ }, "Windows": { "url": "https://raw.githubusercontent.com/Scottcjn/Rustchain/main/miners/windows/rustchain_windows_miner.py", - "sha256": "5b69ebc210e4e8e32975b711dcb1ca08e07b731ddfe4f9f2f9a7e68c1e246a9d", + "sha256": "7f663904031e5a4202be416682fd16ab51af2e96664d6db1567f716d8625f8e1", }, } diff --git a/tests/test_api.py b/tests/test_api.py index 68d7210e4..2c8687d89 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -82,6 +82,7 @@ def test_api_miners_requires_auth(client): rows_result = MagicMock() rows_result.fetchall.return_value = [] mock_cursor.execute.side_effect = [count_result, rows_result] + mock_connect.return_value.execute.return_value.fetchone.return_value = [0] response = client.get('/api/miners') assert response.status_code == 200 diff --git a/tests/test_beacon_atlas_behavior.py b/tests/test_beacon_atlas_behavior.py index 17fe91f0c..5f5952d3b 100644 --- a/tests/test_beacon_atlas_behavior.py +++ b/tests/test_beacon_atlas_behavior.py @@ -176,7 +176,7 @@ def test_create_contract_workflow(self): contract_id = created['id'] # Verify contract appears in list - list_response = self.client.get('/api/contracts') + list_response = self.client.get('/api/contracts', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) self.assertEqual(list_response.status_code, 200) contracts = json.loads(list_response.data) self.assertEqual(len(contracts), 1) @@ -198,7 +198,7 @@ def test_create_contract_workflow(self): self.assertEqual(update_response.status_code, 200) # Verify state changed - list_response2 = self.client.get('/api/contracts') + list_response2 = self.client.get('/api/contracts', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) contracts2 = json.loads(list_response2.data) self.assertEqual(contracts2[0]['state'], 'active') @@ -282,7 +282,7 @@ def test_bounty_lifecycle_workflow(self): conn.commit() # Get bounties list - response = self.client.get('/api/bounties') + response = self.client.get('/api/bounties', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) self.assertEqual(response.status_code, 200) bounties = json.loads(response.data) self.assertEqual(len(bounties), 1) @@ -298,7 +298,7 @@ def test_bounty_lifecycle_workflow(self): self.assertEqual(claim_response.status_code, 200) # Verify claimed state - response2 = self.client.get('/api/bounties') + response2 = self.client.get('/api/bounties', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) bounties2 = json.loads(response2.data) # Bounty should no longer appear in open list (state changed to claimed) @@ -346,7 +346,7 @@ def test_reputation_tracking_workflow(self): conn.commit() # Get all reputations - response = self.client.get('/api/reputation') + response = self.client.get('/api/reputation', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) self.assertEqual(response.status_code, 200) reps = json.loads(response.data) self.assertEqual(len(reps), 1) @@ -354,13 +354,13 @@ def test_reputation_tracking_workflow(self): self.assertEqual(reps[0]['score'], 50) # Get single agent reputation - response2 = self.client.get('/api/reputation/bcn_reputation_test') + response2 = self.client.get('/api/reputation/bcn_reputation_test', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) self.assertEqual(response2.status_code, 200) rep = json.loads(response2.data) self.assertEqual(rep['bounties_completed'], 2) # Non-existent agent returns 404 - response3 = self.client.get('/api/reputation/bcn_nonexistent') + response3 = self.client.get('/api/reputation/bcn_nonexistent', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) self.assertEqual(response3.status_code, 404) def test_chat_message_storage(self): @@ -461,7 +461,7 @@ def test_recipient_can_reject_offered_contract(self): self.assertEqual(reject_response.status_code, 200) self.assertEqual(json.loads(reject_response.data)['state'], 'rejected') - list_response = self.client.get('/api/contracts') + list_response = self.client.get('/api/contracts', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) contracts = json.loads(list_response.data) self.assertEqual(contracts[0]['state'], 'rejected') @@ -514,7 +514,7 @@ def test_creator_cannot_reject_offered_contract(self): ) self.assertEqual(reject_response.status_code, 403) - list_response = self.client.get('/api/contracts') + list_response = self.client.get('/api/contracts', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) contracts = json.loads(list_response.data) self.assertEqual(contracts[0]['state'], 'offered') @@ -576,7 +576,7 @@ def test_bounty_completion_updates_reputation(self): self.assertEqual(complete_response.status_code, 200) # Verify reputation was created/updated - rep_response = self.client.get('/api/reputation/bcn_completer') + rep_response = self.client.get('/api/reputation/bcn_completer', headers={'X-Admin-Key': os.environ.get('RC_ADMIN_KEY', '0'*32)}) self.assertEqual(rep_response.status_code, 200) rep = json.loads(rep_response.data) self.assertEqual(rep['bounties_completed'], 1) diff --git a/tests/test_bridge_lock_ledger.py b/tests/test_bridge_lock_ledger.py index 6ebdcf929..14be37682 100644 --- a/tests/test_bridge_lock_ledger.py +++ b/tests/test_bridge_lock_ledger.py @@ -166,11 +166,11 @@ def funded_miner(setup_test_db): conn = sqlite3.connect(setup_test_db['db_path']) conn.execute( "INSERT INTO balances (miner_id, amount_i64) VALUES (?, ?)", - ("RTC_test_miner", 100 * 1000000) # 100 RTC + ("RTC0000000000000000000000000000000000000000", 100 * 1000000) # 100 RTC ) conn.commit() conn.close() - return "RTC_test_miner" + return "RTC0000000000000000000000000000000000000000" def assert_generic_database_error(result): @@ -193,7 +193,7 @@ def test_valid_deposit_request(self, setup_test_db): "direction": "deposit", "source_chain": "rustchain", "dest_chain": "solana", - "source_address": "RTC_test123", + "source_address": "RTC0000000000000000000000000000000000000000", "dest_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", "amount_rtc": 10.0 } @@ -209,7 +209,7 @@ def test_valid_withdraw_request(self, setup_test_db): "source_chain": "solana", "dest_chain": "rustchain", "source_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", - "dest_address": "RTC_test123", + "dest_address": "RTC0000000000000000000000000000000000000000", "amount_rtc": 5.0 } result = bridge_api.validate_bridge_request(data) @@ -221,7 +221,7 @@ def test_missing_required_field(self, setup_test_db): data = { "direction": "deposit", "dest_chain": "solana", - "source_address": "RTC_test123", + "source_address": "RTC0000000000000000000000000000000000000000", "dest_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", "amount_rtc": 10.0 } @@ -236,7 +236,7 @@ def test_invalid_direction(self, setup_test_db): "direction": "invalid", "source_chain": "rustchain", "dest_chain": "solana", - "source_address": "RTC_test123", + "source_address": "RTC0000000000000000000000000000000000000000", "dest_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", "amount_rtc": 10.0 } @@ -251,8 +251,8 @@ def test_same_chain_fails(self, setup_test_db): "direction": "deposit", "source_chain": "rustchain", "dest_chain": "rustchain", - "source_address": "RTC_test123", - "dest_address": "RTC_other123", + "source_address": "RTC0000000000000000000000000000000000000000", + "dest_address": "RTC11111111111111111111111111111111111111111", "amount_rtc": 10.0 } result = bridge_api.validate_bridge_request(data) @@ -267,7 +267,7 @@ def test_deposit_must_start_from_rustchain(self, setup_test_db): "source_chain": "solana", "dest_chain": "rustchain", "source_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", - "dest_address": "RTC_test123", + "dest_address": "RTC0000000000000000000000000000000000000000", "amount_rtc": 10.0 } result = bridge_api.validate_bridge_request(data) @@ -281,7 +281,7 @@ def test_withdraw_must_end_on_rustchain(self, setup_test_db): "direction": "withdraw", "source_chain": "rustchain", "dest_chain": "solana", - "source_address": "RTC_test123", + "source_address": "RTC0000000000000000000000000000000000000000", "dest_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", "amount_rtc": 10.0 } @@ -296,7 +296,7 @@ def test_amount_below_minimum(self, setup_test_db): "direction": "deposit", "source_chain": "rustchain", "dest_chain": "solana", - "source_address": "RTC_test123", + "source_address": "RTC0000000000000000000000000000000000000000", "dest_address": "4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", "amount_rtc": 0.5 } @@ -315,7 +315,7 @@ class TestAddressValidation: def test_valid_rustchain_address(self, setup_test_db): """Test valid RustChain address.""" bridge_api = setup_test_db["bridge_api"] - valid, msg = bridge_api.validate_chain_address_format("rustchain", "RTC_test123abc") + valid, msg = bridge_api.validate_chain_address_format("rustchain", "RTC0000000000000000000000000000000000000000") assert valid is True def test_invalid_rustchain_address_prefix(self, setup_test_db): @@ -405,7 +405,7 @@ def test_create_withdraw_transfer(self, setup_test_db): source_chain="solana", dest_chain="rustchain", source_address="4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", - dest_address="RTC_dest123", + dest_address="RTC33333333333333333333333333333333333333333", amount_rtc=5.0 ) @@ -447,7 +447,7 @@ def test_admin_bypasses_balance_check(self, setup_test_db): direction="deposit", source_chain="rustchain", dest_chain="solana", - source_address="RTC_unfunded_miner", + source_address="RTC22222222222222222222222222222222222222222", dest_address="4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", amount_rtc=1000.0 ) @@ -469,7 +469,7 @@ def test_database_errors_do_not_leak_details(self, setup_test_db): source_chain="solana", dest_chain="rustchain", source_address="4TRwNqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXq", - dest_address="RTC_dest123", + dest_address="RTC33333333333333333333333333333333333333333", amount_rtc=5.0 ) @@ -648,7 +648,7 @@ def test_database_errors_do_not_leak_details(self, setup_test_db): success, result = lock_ledger.create_lock( conn, - miner_id="RTC_test_miner", + miner_id="RTC0000000000000000000000000000000000000000", amount_i64=10 * 1000000, lock_type="bridge_deposit", unlock_at=int(time.time()) + 3600 @@ -787,6 +787,7 @@ class TestLockLedgerRoutes: """Test lock ledger route-level validation and helper dispatch.""" def _client(self, lock_ledger, db_path): + os.environ["RC_ADMIN_KEY"] = "expected-admin" lock_ledger.DB_PATH = db_path app = Flask(__name__) lock_ledger.register_lock_ledger_routes(app) @@ -806,7 +807,7 @@ def test_miner_locks_rejects_malformed_limit(self, setup_test_db, funded_miner): lock_ledger = setup_test_db["lock_ledger"] client = self._client(lock_ledger, setup_test_db["db_path"]) - response = client.get(f"/api/lock/miner/{funded_miner}?limit=abc") + response = client.get(f"/api/lock/miner/{funded_miner}?limit=abc", headers={"X-Admin-Key": "expected-admin"}) assert response.status_code == 400 assert response.get_json() == {"error": "limit must be an integer"} @@ -815,7 +816,7 @@ def test_pending_unlock_rejects_malformed_before(self, setup_test_db): lock_ledger = setup_test_db["lock_ledger"] client = self._client(lock_ledger, setup_test_db["db_path"]) - response = client.get("/api/lock/pending-unlock?before=not-a-timestamp") + response = client.get("/api/lock/pending-unlock?before=not-a-timestamp", headers={"X-Admin-Key": "expected-admin"}) assert response.status_code == 400 assert response.get_json() == {"error": "before must be an integer"} @@ -842,7 +843,7 @@ def test_pending_unlock_route_calls_database_helper(self, setup_test_db, funded_ client = self._client(lock_ledger, db_path) - response = client.get("/api/lock/pending-unlock?limit=10") + response = client.get("/api/lock/pending-unlock?limit=10", headers={"X-Admin-Key": "expected-admin"}) assert response.status_code == 200 body = response.get_json() @@ -872,7 +873,7 @@ def test_pending_unlock_before_zero_applies_cutoff(self, setup_test_db, funded_m client = self._client(lock_ledger, db_path) - response = client.get("/api/lock/pending-unlock?before=0&limit=10") + response = client.get("/api/lock/pending-unlock?before=0&limit=10", headers={"X-Admin-Key": "expected-admin"}) assert response.status_code == 200 body = response.get_json() diff --git a/tests/test_governance_api.py b/tests/test_governance_api.py index 658d892c6..7015c74b1 100644 --- a/tests/test_governance_api.py +++ b/tests/test_governance_api.py @@ -44,16 +44,27 @@ def test_governance_propose_requires_gt_10_rtc_balance(): integrated_node.app.config["DB_PATH"] = db_path integrated_node.init_db() + pub_hex = "11" * 32 + wallet = integrated_node.address_from_pubkey(pub_hex) + with sqlite3.connect(db_path) as c: - c.execute("INSERT INTO balances(miner_pk, balance_rtc) VALUES(?, ?)", ("RTC-low", 10.0)) + c.execute("INSERT INTO balances(miner_pk, balance_rtc) VALUES(?, ?)", (wallet, 10.0)) c.commit() integrated_node.app.config["TESTING"] = True with integrated_node.app.test_client() as client: - resp = client.post( - "/governance/propose", - json={"wallet": "RTC-low", "title": "No", "description": "insufficient"}, - ) + with patch("integrated_node.verify_rtc_signature", return_value=True): + resp = client.post( + "/governance/propose", + json={ + "wallet": wallet, + "title": "No", + "description": "insufficient", + "nonce": "n-1", + "signature": "ab" * 64, + "public_key": pub_hex, + }, + ) assert resp.status_code == 403 assert resp.get_json()["error"] == "insufficient_balance_to_propose" @@ -136,10 +147,18 @@ def test_governance_vote_flow_and_lifecycle_finalization(): integrated_node.app.config["TESTING"] = True with integrated_node.app.test_client() as client: # Create proposal - r1 = client.post( - "/governance/propose", - json={"wallet": wallet, "title": "Raise testnet fee", "description": "for anti-spam"}, - ) + with patch("integrated_node.verify_rtc_signature", return_value=True): + r1 = client.post( + "/governance/propose", + json={ + "wallet": wallet, + "title": "Raise testnet fee", + "description": "for anti-spam", + "nonce": "n-1", + "signature": "ab" * 64, + "public_key": pub_hex, + }, + ) assert r1.status_code == 201 proposal_id = r1.get_json()["proposal"]["id"] diff --git a/tests/test_gpu_render_protocol.py b/tests/test_gpu_render_protocol.py index 69e146c44..a2d476ee5 100644 --- a/tests/test_gpu_render_protocol.py +++ b/tests/test_gpu_render_protocol.py @@ -327,7 +327,7 @@ def test_gpu_protocol_attest_requires_admin_key_before_write(tmp_path, monkeypat assert response.status_code == 401 assert response.get_json() == {"error": "Unauthorized - admin key required"} - nodes = client.get("/gpu/nodes").get_json() + nodes = client.get("/gpu/nodes", headers={"X-Admin-Key": "test-admin-key"}).get_json() assert nodes["count"] == 0 @@ -348,7 +348,8 @@ def test_gpu_protocol_attest_fails_closed_without_admin_key(tmp_path, monkeypatc assert response.status_code == 503 assert response.get_json() == {"error": "RC_ADMIN_KEY not configured"} - nodes = client.get("/gpu/nodes").get_json() + monkeypatch.setenv("RC_ADMIN_KEY", "test-admin-key") + nodes = client.get("/gpu/nodes", headers={"X-Admin-Key": "test-admin-key"}).get_json() assert nodes["count"] == 0 @@ -369,7 +370,7 @@ def test_gpu_protocol_attest_accepts_api_key_header(tmp_path, monkeypatch): assert response.status_code == 200 assert response.get_json()["status"] == "attested" - nodes = client.get("/gpu/nodes").get_json() + nodes = client.get("/gpu/nodes", headers={"X-Admin-Key": "test-admin-key"}).get_json() assert nodes["count"] == 1 diff --git a/tests/test_miner_headerkey_schema.py b/tests/test_miner_headerkey_schema.py index 306969e4d..c9d168bfa 100644 --- a/tests/test_miner_headerkey_schema.py +++ b/tests/test_miner_headerkey_schema.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: MIT """Regression tests for miner header key schema initialisation.""" +import os import sys @@ -18,7 +19,7 @@ def test_init_db_creates_miner_header_keys_table(tmp_path): with node.app.test_client() as client: response = client.post( "/miner/headerkey", - headers={"X-API-Key": "0" * 32}, + headers={"X-API-Key": os.environ["RC_ADMIN_KEY"]}, json={"miner_id": "miner-one", "pubkey_hex": "a" * 64}, ) diff --git a/tests/test_otc_bridge_htlc_preimage.py b/tests/test_otc_bridge_htlc_preimage.py index 60e0022ad..53f62461e 100644 --- a/tests/test_otc_bridge_htlc_preimage.py +++ b/tests/test_otc_bridge_htlc_preimage.py @@ -182,7 +182,9 @@ def test_buy_order_defers_htlc_secret_to_matching_seller(tmp_path): assert "htlc_secret" not in public_order with patch.object(module.requests, "post") as mock_post: - mock_post.return_value = MagicMock(ok=True, text='{"ok": true}') + mock_response = MagicMock(ok=True, text='{"ok": true}') + mock_response.json.return_value = {"ok": True, "details": {"phase": "pending", "pending_id": "test_pending_id", "tx_hash": "test_tx_hash"}} + mock_post.return_value = mock_response confirm_response = client.post( f"/api/orders/{order['order_id']}/confirm", json={ diff --git a/tests/test_rustchain_monitor.py b/tests/test_rustchain_monitor.py index 0750975d5..d02853738 100644 --- a/tests/test_rustchain_monitor.py +++ b/tests/test_rustchain_monitor.py @@ -176,6 +176,17 @@ def test_print_miners_accepts_paginated_envelope(rustchain_monitor_module, capsy assert "Unexpected response" not in output +def test_print_miners_accepts_list_and_miners_envelope(rustchain_monitor_module, capsys): + rustchain_monitor_module.print_miners([{"miner": "list-miner"}]) + rustchain_monitor_module.print_miners({"miners": [{"miner": "envelope-miner"}]}) + + output = capsys.readouterr().out + assert "Active miners: 1" in output + assert "list-miner" in output + assert "envelope-miner" in output + assert "Unexpected response" not in output + + def test_print_epoch_formats_success_and_error(rustchain_monitor_module, capsys): rustchain_monitor_module.print_epoch( { diff --git a/tests/test_tx_handler_error_redaction.py b/tests/test_tx_handler_error_redaction.py index e8590280d..07765dcb0 100644 --- a/tests/test_tx_handler_error_redaction.py +++ b/tests/test_tx_handler_error_redaction.py @@ -4,12 +4,16 @@ Regression tests for transaction API internal error redaction. """ +import os + from flask import Flask from node.rustchain_tx_handler import create_tx_api_routes LEAKY_ERROR = "no such table: pending_transactions at /srv/rustchain/prod.db" +ADMIN_KEY = "test-admin-key-0123456789abcdef" +ADMIN_HEADERS = {"X-Admin-Key": ADMIN_KEY} class ExplodingPool: @@ -41,6 +45,7 @@ def _get_pending_nonces(self, address): def _client_for_exploding_pool(): + os.environ["RC_ADMIN_KEY"] = ADMIN_KEY app = Flask(__name__) app.config["TESTING"] = True create_tx_api_routes(app, ExplodingPool()) @@ -56,22 +61,22 @@ def _assert_redacted(response): def test_tx_status_redacts_internal_exception_details(): with _client_for_exploding_pool() as client: - _assert_redacted(client.get("/tx/status/hash_1")) + _assert_redacted(client.get("/tx/status/hash_1", headers=ADMIN_HEADERS)) def test_tx_pending_redacts_internal_exception_details(): with _client_for_exploding_pool() as client: - _assert_redacted(client.get("/tx/pending")) + _assert_redacted(client.get("/tx/pending", headers=ADMIN_HEADERS)) def test_wallet_balance_redacts_internal_exception_details(): with _client_for_exploding_pool() as client: - _assert_redacted(client.get("/wallet/alice/balance")) + _assert_redacted(client.get("/wallet/alice/balance", headers=ADMIN_HEADERS)) def test_wallet_nonce_redacts_internal_exception_details(): with _client_for_exploding_pool() as client: - _assert_redacted(client.get("/wallet/alice/nonce")) + _assert_redacted(client.get("/wallet/alice/nonce", headers=ADMIN_HEADERS)) def test_wallet_history_redacts_internal_exception_details(monkeypatch): @@ -83,4 +88,4 @@ def raise_connect_error(*args, **kwargs): monkeypatch.setattr(tx_handler.sqlite3, "connect", raise_connect_error) with _client_for_exploding_pool() as client: - _assert_redacted(client.get("/wallet/alice/history")) + _assert_redacted(client.get("/wallet/alice/history", headers=ADMIN_HEADERS)) diff --git a/tests/test_tx_handler_limits.py b/tests/test_tx_handler_limits.py index 8566b03ae..afa8433b7 100644 --- a/tests/test_tx_handler_limits.py +++ b/tests/test_tx_handler_limits.py @@ -8,9 +8,11 @@ """ import os +import gc import json import sqlite3 import tempfile +import time import pytest from flask import Flask from node.rustchain_tx_handler import TransactionPool, create_tx_api_routes @@ -27,6 +29,7 @@ def app_context(monkeypatch): monkeypatch.setenv("RC_ADMIN_KEY", admin_key) db_fd, db_path = tempfile.mkstemp() + os.close(db_fd) app = Flask(__name__) app.config['TESTING'] = True @@ -50,8 +53,18 @@ def app_context(monkeypatch): client.environ_base['HTTP_X_ADMIN_KEY'] = admin_key yield client - os.close(db_fd) - os.unlink(db_path) + del client + del pool + gc.collect() + + for attempt in range(5): + try: + os.unlink(db_path) + break + except PermissionError: + if attempt == 4: + raise + time.sleep(0.1) def test_pending_default_limit(app_context): """Scenario: Default parameters (no query string) - Expect 100 (from logic)""" diff --git a/tests/test_wallet_review_holds.py b/tests/test_wallet_review_holds.py index dc6bd94cc..c79ac7e0d 100644 --- a/tests/test_wallet_review_holds.py +++ b/tests/test_wallet_review_holds.py @@ -123,6 +123,7 @@ def client(monkeypatch): db_path = local_tmp_dir / f"{uuid.uuid4().hex}.sqlite3" _init_attestation_db(db_path) + monkeypatch.setenv("RC_ADMIN_KEY", "0" * 32) monkeypatch.setattr(integrated_node, "DB_PATH", str(db_path)) monkeypatch.setattr(integrated_node, "HW_BINDING_V2", False, raising=False) monkeypatch.setattr(integrated_node, "HW_PROOF_AVAILABLE", False, raising=False) diff --git a/tools/rustchain-monitor/rustchain_monitor.py b/tools/rustchain-monitor/rustchain_monitor.py index e4c4a270c..503e72e37 100755 --- a/tools/rustchain-monitor/rustchain_monitor.py +++ b/tools/rustchain-monitor/rustchain_monitor.py @@ -77,13 +77,6 @@ def get_epoch(): except Exception as e: return {"error": str(e)} -def normalize_miners_payload(data): - if isinstance(data, list): - return data - if isinstance(data, dict) and isinstance(data.get("miners"), list): - return data["miners"] - return None - def print_health(data): if "error" in data: print(f"❌ Health check failed: {data['error']}") @@ -112,7 +105,7 @@ def print_miners(data): print(f"❌ Failed to fetch miners: {data['error']}") return miners = normalize_miners_payload(data) - if miners is None: + if miners is None or not isinstance(miners, list): print(f"⚠ Unexpected response: {data}") return print(f"πŸ“Š Active miners: {len(miners)}")