Skip to content

Latest commit

 

History

History
684 lines (540 loc) · 12.3 KB

File metadata and controls

684 lines (540 loc) · 12.3 KB

API Reference

Complete API documentation for Bounty Challenge.

Base URLs

Bridge API (Recommended)

All requests should go through the platform bridge:

https://chain.platform.network/api/v1/bridge/bounty-challenge/

Direct Server (Development)

For local development or direct server access:

http://localhost:8080/

Authentication

Signature-Based Authentication

Requests that modify state require sr25519 signatures:

message = "{action}:{data}:{timestamp}"
signature = sr25519_sign(message, secret_key)

Timestamp Validation

  • Timestamps must be within 5 minutes of server time
  • Uses Unix timestamps (seconds since epoch)
  • Prevents replay attacks

Endpoints

Health Check

Check if the server is healthy.

GET /health

Response:

{
  "healthy": true,
  "load": 0.0,
  "pending": 0,
  "uptime_secs": 3600,
  "version": "0.1.0",
  "challenge_id": "bounty-challenge"
}

Register

Register a GitHub username with a hotkey.

POST /register

Request Body:

{
  "hotkey": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "github_username": "johndoe",
  "signature": "0x...",
  "timestamp": 1705590000
}

Signature Message Format:

register_github:{github_username_lowercase}:{timestamp}

Success Response (200):

{
  "success": true,
  "message": "Successfully registered @johndoe with your hotkey."
}

Error Response (400):

{
  "success": false,
  "error": "Invalid signature. Make sure you're using the correct key."
}

Possible Errors:

Error Cause
Timestamp expired Request older than 5 minutes
Invalid signature Signature doesn't match hotkey
Registration failed Database error

Status

Get status for a specific hotkey.

GET /status/{hotkey}

Path Parameters:

Parameter Type Description
hotkey string SS58-encoded hotkey

Response:

{
  "registered": true,
  "github_username": "johndoe",
  "valid_issues_count": 5,
  "invalid_issues_count": 2,
  "balance": 3,
  "is_penalized": false,
  "weight": 0.05
}

Not Registered Response:

{
  "registered": false,
  "github_username": null,
  "valid_issues_count": null,
  "invalid_issues_count": null,
  "balance": null,
  "is_penalized": false,
  "weight": null
}

Leaderboard

Get current standings.

GET /leaderboard

Query Parameters:

Parameter Type Default Description
limit integer 20 Number of entries to return

Response:

{
  "leaderboard": [
    {
      "github_username": "alice",
      "hotkey": "5GrwvaEF...",
      "issues_resolved_24h": 12,
      "weight": 0.12
    },
    {
      "github_username": "bob",
      "hotkey": "5FHneW46...",
      "issues_resolved_24h": 8,
      "weight": 0.08
    }
  ]
}

Stats

Get challenge statistics.

GET /stats

Response:

{
  "total_bounties": 150,
  "total_miners": 25,
  "total_invalid": 10,
  "penalized_miners": 3,
  "challenge_id": "bounty-challenge",
  "version": "0.1.0"
}

Record Invalid Issue

Record an invalid issue (maintainers only).

POST /invalid

Request Body:

{
  "issue_id": 123,
  "repo_owner": "PlatformNetwork",
  "repo_name": "bounty-challenge",
  "github_username": "johndoe",
  "issue_url": "https://github.com/PlatformNetwork/bounty-challenge/issues/123",
  "issue_title": "Optional title",
  "reason": "Optional reason for marking invalid"
}

Success Response (200):

{
  "success": true,
  "message": "Recorded invalid issue #123 by @johndoe"
}

Error Response:

{
  "success": false,
  "error": "Failed to record invalid issue: <error message>"
}

List Issues

Get a list of issues from the cache.

GET /issues

Query Parameters:

Parameter Type Default Description
state string - Filter by issue state (open, closed)
label string - Filter by label
limit integer 100 Maximum number of issues to return (max: 1000)
offset integer 0 Number of issues to skip

Response:

{
  "issues": [
    {
      "id": 123,
      "title": "Issue title",
      "state": "closed",
      "labels": ["valid"],
      "user": "johndoe",
      "created_at": "2025-01-15T10:00:00Z",
      "closed_at": "2025-01-16T15:30:00Z"
    }
  ],
  "count": 10,
  "limit": 100,
  "offset": 0
}

List Pending Issues

Get a list of pending (unprocessed) issues.

GET /issues/pending

Query Parameters:

Parameter Type Default Description
limit integer 100 Maximum number of issues to return (max: 1000)
offset integer 0 Number of issues to skip

Response:

{
  "issues": [
    {
      "id": 456,
      "title": "Pending issue title",
      "state": "open",
      "labels": [],
      "user": "alice",
      "created_at": "2025-01-17T08:00:00Z"
    }
  ],
  "count": 5,
  "limit": 100,
  "offset": 0
}

Issues Statistics

Get statistics about issues.

GET /issues/stats

Response:

{
  "total_issues": 500,
  "open_issues": 50,
  "closed_issues": 450,
  "valid_issues": 200,
  "invalid_issues": 30,
  "pending_issues": 20
}

Hotkey Details

Get detailed information for a specific hotkey.

GET /hotkey/{hotkey}

Path Parameters:

Parameter Type Description
hotkey string SS58-encoded hotkey

Response:

{
  "hotkey": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "github_username": "johndoe",
  "registered_at": "2025-01-10T12:00:00Z",
  "valid_issues_count": 15,
  "invalid_issues_count": 2,
  "balance": 13,
  "is_penalized": false,
  "weight": 0.15,
  "recent_issues": [
    {
      "id": 123,
      "title": "Fixed bug in API",
      "resolved_at": "2025-01-16T15:30:00Z"
    }
  ]
}

Not Found Response:

{
  "error": "Hotkey not found"
}

GitHub User Details

Get detailed information for a GitHub user.

GET /github/{username}

Path Parameters:

Parameter Type Description
username string GitHub username

Response:

{
  "github_username": "johndoe",
  "hotkey": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "registered_at": "2025-01-10T12:00:00Z",
  "valid_issues_count": 15,
  "invalid_issues_count": 2,
  "balance": 13,
  "is_penalized": false,
  "weight": 0.15,
  "recent_issues": [
    {
      "id": 123,
      "title": "Fixed bug in API",
      "resolved_at": "2025-01-16T15:30:00Z"
    }
  ]
}

Not Found Response:

{
  "error": "GitHub user not found"
}

Sync Status

Get the current synchronization status for all repositories.

GET /sync/status

Response:

{
  "repos": [
    {
      "owner": "PlatformNetwork",
      "repo": "bounty-challenge",
      "last_synced_at": "2025-01-17T10:00:00Z",
      "issues_count": 150,
      "status": "synced"
    }
  ],
  "issues_stats": {
    "total_issues": 500,
    "open_issues": 50,
    "closed_issues": 450
  }
}

Trigger Sync

Trigger a manual synchronization of issues from GitHub.

POST /sync/trigger

Response (Success):

{
  "success": true,
  "issues_synced": 150,
  "errors": []
}

Response (Partial Failure):

{
  "success": false,
  "issues_synced": 100,
  "errors": [
    "PlatformNetwork/other-repo: rate limit exceeded"
  ]
}

Get Weights

Get current weight calculations for all miners.

GET /get_weights

Query Parameters:

Parameter Type Default Description
epoch integer current Epoch number

Response:

{
  "weights": [
    {
      "hotkey": "5GrwvaEF...",
      "weight": 0.35
    },
    {
      "hotkey": "5FHneW46...",
      "weight": 0.25
    }
  ],
  "epoch": 12345,
  "challenge_id": "bounty-challenge",
  "total_miners": 15
}

Config

Get challenge configuration.

GET /config

Response:

{
  "challenge_id": "bounty-challenge",
  "name": "Bounty Challenge",
  "version": "0.1.0",
  "config_schema": {
    "type": "object",
    "properties": {
      "action": {
        "type": "string",
        "enum": ["register", "claim", "leaderboard"]
      },
      "github_username": {
        "type": "string"
      }
    }
  },
  "features": ["github-verification", "anti-abuse"],
  "limits": {
    "max_submission_size": 10240,
    "max_evaluation_time": 60
  }
}

Error Handling

Error Response Format

{
  "success": false,
  "error": "Error message here"
}

HTTP Status Codes

Code Meaning
200 Success
400 Bad Request (validation error)
401 Unauthorized (invalid signature)
404 Not Found
500 Internal Server Error

Common Errors

Error Message Cause Solution
Timestamp expired Request too old Use current timestamp
Invalid signature Wrong key used Verify secret key
Registration failed DB error Retry or contact support
Hotkey not found Not registered Register first

Rate Limits

Endpoint Limit
/register 10/minute
/status/* 60/minute
/leaderboard 30/minute
/stats 30/minute

Code Examples

Python

import requests
import time
from substrateinterface import Keypair

# Create keypair from seed
keypair = Keypair.create_from_mnemonic("your mnemonic here")

# Prepare registration
timestamp = int(time.time())
message = f"register_github:johndoe:{timestamp}"
signature = keypair.sign(message.encode()).hex()

# Register
response = requests.post(
    "https://chain.platform.network/api/v1/bridge/bounty-challenge/register",
    json={
        "hotkey": keypair.ss58_address,
        "github_username": "johndoe",
        "signature": f"0x{signature}",
        "timestamp": timestamp
    }
)

print(response.json())

Rust

use sp_core::{sr25519, Pair};
use reqwest::Client;
use serde_json::json;

async fn register(secret_key: &str, github_username: &str) -> Result<(), Box<dyn std::error::Error>> {
    let pair = sr25519::Pair::from_string(secret_key, None)?;
    let timestamp = chrono::Utc::now().timestamp();
    
    let message = format!("register_github:{}:{}", github_username.to_lowercase(), timestamp);
    let signature = pair.sign(message.as_bytes());
    
    let client = Client::new();
    let response = client
        .post("https://chain.platform.network/api/v1/bridge/bounty-challenge/register")
        .json(&json!({
            "hotkey": encode_ss58(&pair.public().0),
            "github_username": github_username,
            "signature": hex::encode(signature.0),
            "timestamp": timestamp
        }))
        .send()
        .await?;
    
    println!("{}", response.text().await?);
    Ok(())
}

JavaScript

const { Keyring } = require('@polkadot/keyring');
const { u8aToHex } = require('@polkadot/util');

async function register(mnemonic, githubUsername) {
    const keyring = new Keyring({ type: 'sr25519' });
    const pair = keyring.addFromMnemonic(mnemonic);
    
    const timestamp = Math.floor(Date.now() / 1000);
    const message = `register_github:${githubUsername.toLowerCase()}:${timestamp}`;
    const signature = pair.sign(message);
    
    const response = await fetch(
        'https://chain.platform.network/api/v1/bridge/bounty-challenge/register',
        {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                hotkey: pair.address,
                github_username: githubUsername,
                signature: u8aToHex(signature),
                timestamp: timestamp
            })
        }
    );
    
    console.log(await response.json());
}

WebSocket API

Coming soon: Real-time updates via WebSocket

wss://chain.platform.network/api/v1/ws/bounty-challenge

Events:

  • issue_validated - New issue validated
  • weight_updated - Weights recalculated
  • leaderboard_changed - Rankings changed