chore: initialize monorepo with project scaffolding, configs, and CI setup

This commit is contained in:
oib
2025-09-27 06:05:25 +02:00
commit c1926136fb
171 changed files with 13708 additions and 0 deletions

View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
export PYTHONPATH="${ROOT_DIR}/src:${ROOT_DIR}/scripts:${PYTHONPATH:-}"
GENESIS_PATH="${ROOT_DIR}/data/devnet/genesis.json"
python "${ROOT_DIR}/scripts/make_genesis.py" --output "${GENESIS_PATH}" --force
echo "[devnet] Generated genesis at ${GENESIS_PATH}"
declare -a CHILD_PIDS=()
cleanup() {
for pid in "${CHILD_PIDS[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
fi
done
}
trap cleanup EXIT
python -m aitbc_chain.main &
CHILD_PIDS+=($!)
echo "[devnet] Blockchain node started (PID ${CHILD_PIDS[-1]})"
sleep 1
python -m uvicorn aitbc_chain.app:app --host 127.0.0.1 --port 8080 --log-level info &
CHILD_PIDS+=($!)
echo "[devnet] RPC API serving at http://127.0.0.1:8080"
python -m uvicorn mock_coordinator:app --host 127.0.0.1 --port 8090 --log-level info &
CHILD_PIDS+=($!)
echo "[devnet] Mock coordinator serving at http://127.0.0.1:8090"
wait

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""Generate a pseudo devnet key pair for blockchain components."""
from __future__ import annotations
import argparse
import json
import secrets
from pathlib import Path
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Generate a devnet key pair")
parser.add_argument(
"--output",
type=Path,
help="Optional path to write the keypair JSON (prints to stdout if omitted)",
)
return parser.parse_args()
def generate_keypair() -> dict:
private_key = secrets.token_hex(32)
public_key = secrets.token_hex(32)
address = "ait1" + secrets.token_hex(20)
return {
"private_key": private_key,
"public_key": public_key,
"address": address,
}
def main() -> None:
args = parse_args()
keypair = generate_keypair()
payload = json.dumps(keypair, indent=2)
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(payload + "\n", encoding="utf-8")
print(f"[keygen] wrote keypair to {args.output}")
else:
print(payload)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Generate a deterministic devnet genesis file for the blockchain node."""
from __future__ import annotations
import argparse
import json
import time
from pathlib import Path
DEFAULT_GENESIS = {
"chain_id": "ait-devnet",
"timestamp": None, # populated at runtime
"params": {
"mint_per_unit": 1000,
"coordinator_ratio": 0.05,
"base_fee": 10,
"fee_per_byte": 1,
},
"accounts": [
{
"address": "ait1faucet000000000000000000000000000000000",
"balance": 1_000_000_000,
"nonce": 0,
}
],
"authorities": [
{
"address": "ait1devproposer000000000000000000000000000000",
"weight": 1,
}
],
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Generate devnet genesis data")
parser.add_argument(
"--output",
type=Path,
default=Path("data/devnet/genesis.json"),
help="Path to write the generated genesis file (default: data/devnet/genesis.json)",
)
parser.add_argument(
"--force",
action="store_true",
help="Overwrite the genesis file if it already exists.",
)
parser.add_argument(
"--faucet-address",
default="ait1faucet000000000000000000000000000000000",
help="Address seeded with devnet funds.",
)
parser.add_argument(
"--faucet-balance",
type=int,
default=1_000_000_000,
help="Faucet balance in smallest units.",
)
parser.add_argument(
"--authorities",
nargs="*",
default=["ait1devproposer000000000000000000000000000000"],
help="Authority addresses included in the genesis file.",
)
return parser.parse_args()
def build_genesis(args: argparse.Namespace) -> dict:
genesis = json.loads(json.dumps(DEFAULT_GENESIS)) # deep copy via JSON
genesis["timestamp"] = int(time.time())
genesis["accounts"][0]["address"] = args.faucet_address
genesis["accounts"][0]["balance"] = args.faucet_balance
genesis["authorities"] = [
{"address": address, "weight": 1}
for address in args.authorities
]
return genesis
def write_genesis(path: Path, data: dict, force: bool) -> None:
if path.exists() and not force:
raise SystemExit(f"Genesis file already exists at {path}. Use --force to overwrite.")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print(f"[genesis] wrote genesis file to {path}")
def main() -> None:
args = parse_args()
genesis = build_genesis(args)
write_genesis(args.output, genesis, args.force)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""Mock coordinator API for devnet testing."""
from __future__ import annotations
from typing import Dict
from fastapi import FastAPI
app = FastAPI(title="Mock Coordinator API", version="0.1.0")
MOCK_JOBS: Dict[str, Dict[str, str]] = {
"job_1": {"status": "complete", "price": "50000", "compute_units": 2500},
"job_2": {"status": "complete", "price": "25000", "compute_units": 1200},
}
@app.get("/health")
def health() -> Dict[str, str]:
return {"status": "ok"}
@app.post("/attest/receipt")
def attest_receipt(payload: Dict[str, str]) -> Dict[str, str | bool]:
job_id = payload.get("job_id")
if job_id in MOCK_JOBS:
return {
"exists": True,
"paid": True,
"not_double_spent": True,
"quote": MOCK_JOBS[job_id],
}
return {
"exists": False,
"paid": False,
"not_double_spent": False,
"quote": {},
}