Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added hunter/__init__.py
Empty file.
31 changes: 31 additions & 0 deletions hunter/core/executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import subprocess
import os

class BountyExecutor:
"""
Handles the technical execution of a bounty: fork, clone, implement, commit, PR.
"""
def __init__(self, repo: str, token: str):
self.repo = repo
self.token = token

def fork_and_clone(self):
"""Fork and clone the repository using gh CLI."""
print(f"🍴 Forking {self.repo}...")
subprocess.run(["gh", "repo", "fork", self.repo, "--clone=true"], check=True)

def submit_pr(self, branch: str, title: str, body: str):
"""Create a PR to the upstream repository."""
print(f"🚀 Creating Pull Request: {title}")
subprocess.run([
"gh", "pr", "create",
"--title", title,
"--body", body,
"--head", branch
], check=True)

def commit_changes(self, message: str):
"""Commit local changes."""
subprocess.run(["git", "add", "."], check=True)
subprocess.run(["git", "commit", "-m", message], check=True)
subprocess.run(["git", "push", "origin", "HEAD"], check=True)
34 changes: 34 additions & 0 deletions hunter/core/scanner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import requests
import json
from typing import List, Dict

class BountyScanner:
"""
Scans GitHub repositories for open bounties.
"""
def __init__(self, token: str):
self.headers = {"Authorization": f"token {token}", "Accept": "application/vnd.github.v3+json"}
self.base_url = "https://api.github.com"

def find_bounties(self, repo: str) -> List[Dict]:
"""Find open issues with the 'bounty' label."""
url = f"{self.base_url}/repos/{repo}/issues"
params = {"labels": "bounty", "state": "open"}
r = requests.get(url, headers=self.headers, params=params)
r.raise_for_status()
return r.json()

def evaluate_difficulty(self, issue: Dict) -> int:
"""
Heuristic evaluation of issue difficulty (1-10).
Matches keywords against agent capabilities.
"""
text = (issue.get('title', '') + issue.get('body', '')).lower()
score = 5 # Default

# Capability match
if "python" in text: score -= 2
if "sdk" in text: score -= 1
if "vintage" in text or "hardware" in text: score += 4 # Hard for pure AI

return max(1, min(10, score))
25 changes: 25 additions & 0 deletions hunter/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from core.scanner import BountyScanner
from core.executor import BountyExecutor
import os

def run_hunter():
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
TARGET_REPO = "Scottcjn/rustchain-bounties"

print("🦅 Operative Reon: Hunter Framework Engaged.")

scanner = BountyScanner(GITHUB_TOKEN)
executor = BountyExecutor(TARGET_REPO, GITHUB_TOKEN)

# 1. Scan
bounties = scanner.find_bounties(TARGET_REPO)
print(f"🔍 Found {len(bounties)} open bounties.")

for b in bounties:
diff = scanner.evaluate_difficulty(b)
print(f" - [{b['number']}] {b['title']} (Difficulty: {diff}/10)")

print("\n✅ Framework initialized. Ready for autonomous task assignment.")

if __name__ == "__main__":
run_hunter()
Empty file added hunter/requirements.txt
Empty file.
43 changes: 43 additions & 0 deletions sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# RustChain Python SDK

Official Python client for the [RustChain](https://github.com/Scottcjn/Rustchain) Proof-of-Antiquity network.

## Installation

```bash
pip install rustchain-sdk
```

## Quick Start

```python
from rustchain_sdk import RustChainClient

# Connect to the live node
client = RustChainClient("https://50.28.86.131", verify_ssl=False)

# Check health
status = client.health()
print(f"Node Version: {status.version}")

# Get miner info
miners = client.miners()
for m in miners:
print(f"Miner: {m.miner} | Multiplier: {m.antiquity_multiplier}")

# Check balance
balance = client.balance("your_miner_id")
print(f"Balance: {balance.amount_rtc} RTC")
```

## Features

- **Node Health**: Check version and uptime.
- **Epoch Info**: Track slots and reward pots.
- **Miner List**: Discover active hardware on the network.
- **Wallet Management**: Query balances and perform signed transfers.
- **Attestation**: Submit hardware fingerprints for enrollment.

## License

MIT
21 changes: 21 additions & 0 deletions sdk/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "rustchain-sdk"
version = "0.1.0"
description = "Official Python SDK for the RustChain Proof-of-Antiquity network."
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
"requests>=2.25.0",
]
authors = [
{name = "nighthawks", email = "night@example.com"},
]
license = {text = "MIT"}

[project.urls]
Homepage = "https://github.com/nighthawks0/rustchain-sdk"
Repository = "https://github.com/Scottcjn/Rustchain"
5 changes: 5 additions & 0 deletions sdk/rustchain_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .client import RustChainClient
from .models import HealthStatus, EpochInfo, MinerInfo, Balance, TransferResponse, AttestationResponse
from .exceptions import RustChainError, APIError, AuthenticationError, InsufficientBalanceError, VMDetectedError

__version__ = "0.1.0"
91 changes: 91 additions & 0 deletions sdk/rustchain_sdk/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import requests
from typing import List, Optional, Dict, Any
from .models import HealthStatus, EpochInfo, MinerInfo, Balance, TransferResponse, AttestationResponse
from .exceptions import APIError, AuthenticationError, InsufficientBalanceError, VMDetectedError

class RustChainClient:
"""
Client for interacting with the RustChain node API.
"""
def __init__(self, base_url: str = "https://50.28.86.131", verify_ssl: bool = False):
self.base_url = base_url.rstrip('/')
self.verify_ssl = verify_ssl
self.session = requests.Session()

# Suppress insecure request warnings if verify_ssl is False
if not verify_ssl:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
url = f"{self.base_url}{endpoint}"
try:
response = self.session.request(method, url, verify=self.verify_ssl, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
try:
error_data = response.json()
error_code = error_data.get('error')
detail = error_data.get('detail') or error_data.get('check_failed')

if error_code == 'VM_DETECTED':
raise VMDetectedError(f"Attestation failed: {detail}", code=error_code, detail=detail)
elif error_code == 'INVALID_SIGNATURE':
raise AuthenticationError("Invalid signature provided")
elif error_code == 'INSUFFICIENT_BALANCE':
raise InsufficientBalanceError("Insufficient RTC balance for transfer")

raise APIError(f"API Error: {error_code or e}", code=error_code, detail=detail)
except (ValueError, AttributeError):
raise APIError(f"HTTP Error: {e}")
except Exception as e:
raise APIError(f"Request failed: {e}")

def health(self) -> HealthStatus:
"""Check node health and version."""
data = self._request("GET", "/health")
return HealthStatus(**data)

def epoch(self) -> EpochInfo:
"""Get current epoch details."""
data = self._request("GET", "/epoch")
return EpochInfo(**data)

def miners(self) -> List[MinerInfo]:
"""List all active/enrolled miners."""
data = self._request("GET", "/api/miners")
return [MinerInfo(**item) for item in data]

def balance(self, miner_id: str) -> Balance:
"""Check RTC balance for a miner."""
params = {"miner_id": miner_id}
data = self._request("GET", "/wallet/balance", params=params)
return Balance(**data)

def transfer(self, from_id: str, to_id: str, amount_i64: int, nonce: int, signature: str) -> TransferResponse:
"""
Transfer RTC to another wallet.
Requires an Ed25519 signature of the transfer payload.
"""
payload = {
"from": from_id,
"to": to_id,
"amount_i64": amount_i64,
"nonce": nonce,
"signature": signature
}
data = self._request("POST", "/wallet/transfer/signed", json=payload)
return TransferResponse(**data)

def submit_attestation(self, miner_id: str, fingerprint: Dict[str, Any], signature: str) -> AttestationResponse:
"""
Submit hardware fingerprint for epoch enrollment.
"""
payload = {
"miner_id": miner_id,
"fingerprint": fingerprint,
"signature": signature
}
data = self._request("POST", "/attest/submit", json=payload)
return AttestationResponse(**data)
22 changes: 22 additions & 0 deletions sdk/rustchain_sdk/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class RustChainError(Exception):
"""Base error for RustChain SDK"""
pass

class APIError(RustChainError):
"""Raised when the API returns an error"""
def __init__(self, message, code=None, detail=None):
super().__init__(message)
self.code = code
self.detail = detail

class AuthenticationError(RustChainError):
"""Raised when signature verification fails"""
pass

class InsufficientBalanceError(RustChainError):
"""Raised when wallet has insufficient funds"""
pass

class VMDetectedError(RustChainError):
"""Raised when attestation fails due to VM detection"""
pass
52 changes: 52 additions & 0 deletions sdk/rustchain_sdk/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from dataclasses import dataclass
from typing import List, Optional

@dataclass
class HealthStatus:
ok: bool
version: str
uptime_s: int
db_rw: bool
backup_age_hours: float
tip_age_slots: int

@dataclass
class EpochInfo:
epoch: int
slot: int
blocks_per_epoch: int
epoch_pot: float
enrolled_miners: int

@dataclass
class MinerInfo:
miner: str
device_family: str
device_arch: str
hardware_type: str
antiquity_multiplier: float
entropy_score: float
last_attest: int

@dataclass
class Balance:
miner_id: str
amount_rtc: float
amount_i64: int

@dataclass
class TransferResponse:
success: bool
tx_hash: Optional[str] = None
new_balance: Optional[int] = None
error: Optional[str] = None

@dataclass
class AttestationResponse:
success: bool
enrolled: bool = False
epoch: Optional[int] = None
multiplier: Optional[float] = None
next_settlement_slot: Optional[int] = None
error: Optional[str] = None
detail: Optional[str] = None
59 changes: 59 additions & 0 deletions sdk/test_sdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import unittest
from unittest.mock import patch, MagicMock
from rustchain_sdk import RustChainClient, APIError, AuthenticationError
from rustchain_sdk.models import HealthStatus

class TestRustChainClient(unittest.TestCase):
def setUp(self):
self.client = RustChainClient(base_url="https://mock-node", verify_ssl=False)

@patch('requests.Session.request')
def test_health_success(self, mock_request):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"backup_age_hours": 6.75,
"db_rw": True,
"ok": True,
"tip_age_slots": 0,
"uptime_s": 18728,
"version": "2.2.1-rip200"
}
mock_request.return_value = mock_response

status = self.client.health()
self.assertTrue(status.ok)
self.assertEqual(status.version, "2.2.1-rip200")
self.assertEqual(status.uptime_s, 18728)

@patch('requests.Session.request')
def test_balance_success(self, mock_request):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"amount_i64": 118357193,
"amount_rtc": 118.357193,
"miner_id": "test_miner"
}
mock_request.return_value = mock_response

balance = self.client.balance("test_miner")
self.assertEqual(balance.amount_rtc, 118.357193)
self.assertEqual(balance.miner_id, "test_miner")

@patch('requests.Session.request')
def test_invalid_signature_error(self, mock_request):
mock_response = MagicMock()
mock_response.status_code = 400
mock_response.json.return_value = {"success": False, "error": "INVALID_SIGNATURE"}
mock_request.return_value = mock_response

# We need to trigger raise_for_status for the catch block to work in _request
from requests.exceptions import HTTPError
mock_response.raise_for_status.side_effect = HTTPError(response=mock_response)

with self.assertRaises(AuthenticationError):
self.client.balance("test_miner")

if __name__ == '__main__':
unittest.main()