Skip to content
Merged
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
48 changes: 41 additions & 7 deletions app/src/infinite_hashes/aps_miner/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import asyncio
import os
from collections import Counter
from typing import Any

Expand All @@ -14,10 +15,12 @@

from infinite_hashes.auctions import utils as auction_utils
from infinite_hashes.consensus.bidding import (
MAX_BIDDING_COMMITMENT_WORKER_SIZE_FP18,
MIN_BIDDING_COMMITMENT_WORKER_SIZE_FP18,
BiddingCommitment,
select_auction_winners_async,
)
from infinite_hashes.consensus.price import _parse_decimal_to_fp18_int
from infinite_hashes.consensus.price import _fp18_to_min_decimal_str, _parse_decimal_to_fp18_int

from .callbacks import handle_auction_results
from .config import MinerConfig
Expand Down Expand Up @@ -57,17 +60,48 @@ def _get_hotkey(config: MinerConfig) -> str | None:
def _build_commitment_from_config(config: MinerConfig) -> tuple[str, list[Any]]:
price_fp18 = _parse_decimal_to_fp18_int(config.workers.price_multiplier)

# Auto mode:
# - workers.hashrates list => v=1
# - workers.worker_sizes dict (or hashrates dict normalized into worker_sizes) => v=2
# Always use v2 commitments.
# - workers.worker_sizes dict (or hashrates dict normalized into worker_sizes) => v2
# - workers.hashrates list => aggregate into v2 counts
if config.workers.worker_sizes is not None:
v2_bids = [("BTC", price_fp18, dict(config.workers.worker_sizes))]
commit = BiddingCommitment(t="b", bids=v2_bids, v=2)
return commit.to_compact(), v2_bids

bids = [(hr, price_fp18) for hr in (config.workers.hashrates or [])]
commit = BiddingCommitment(t="b", bids=bids, v=1)
return commit.to_compact(), bids
allow_v1 = os.getenv("APS_MINER_ALLOW_V1", "").strip().lower() in {"1", "true", "yes", "on"}
if allow_v1:
bids = [(hr, price_fp18) for hr in (config.workers.hashrates or [])]
commit = BiddingCommitment(t="b", bids=bids, v=1)
return commit.to_compact(), bids

worker_sizes: dict[str, int] = {}
for hr in config.workers.hashrates or []:
hr_fp = _parse_decimal_to_fp18_int(str(hr))
if hr_fp <= 0:
raise ValueError(f"invalid worker hashrate: {hr}")
full = hr_fp // MAX_BIDDING_COMMITMENT_WORKER_SIZE_FP18
remainder = hr_fp % MAX_BIDDING_COMMITMENT_WORKER_SIZE_FP18

if remainder != 0 and remainder < MIN_BIDDING_COMMITMENT_WORKER_SIZE_FP18:
if full <= 0:
raise ValueError(f"worker hashrate below v2 min size ({MIN_BIDDING_COMMITMENT_WORKER_SIZE_FP18}): {hr}")
delta = MIN_BIDDING_COMMITMENT_WORKER_SIZE_FP18 - remainder
adjusted = MAX_BIDDING_COMMITMENT_WORKER_SIZE_FP18 - delta
full -= 1
remainder = MIN_BIDDING_COMMITMENT_WORKER_SIZE_FP18
adj_key = _fp18_to_min_decimal_str(adjusted)
worker_sizes[adj_key] = worker_sizes.get(adj_key, 0) + 1

if full > 0:
full_key = _fp18_to_min_decimal_str(MAX_BIDDING_COMMITMENT_WORKER_SIZE_FP18)
worker_sizes[full_key] = worker_sizes.get(full_key, 0) + int(full)
if remainder > 0:
rem_key = _fp18_to_min_decimal_str(remainder)
worker_sizes[rem_key] = worker_sizes.get(rem_key, 0) + 1

v2_bids = [("BTC", price_fp18, worker_sizes)]
commit = BiddingCommitment(t="b", bids=v2_bids, v=2)
return commit.to_compact(), v2_bids


def _hashrate_to_fp18(value: Any) -> int | None:
Expand Down
8 changes: 6 additions & 2 deletions app/src/infinite_hashes/auctions/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import structlog
import turbobt

from infinite_hashes.consensus.bidding import BiddingCommitment
from infinite_hashes.consensus.bidding import BiddingCommitment, v2_only_active
from infinite_hashes.consensus.parser import parse_commitment

logger = structlog.get_logger(__name__)
Expand All @@ -28,6 +28,8 @@

def parse_bidding_commitments(
commits: dict[str, bytes | str],
*,
block_number: int | None = None,
) -> dict[str, list[tuple[str, int] | tuple[str, int, int]]]:
"""Parse bidding commitments, safely handling potential binary suffixes.

Expand All @@ -44,6 +46,8 @@ def parse_bidding_commitments(
model = parse_commitment(raw, expected_types=[BiddingCommitment])
if model is None:
continue
if v2_only_active(block_number) and int(getattr(model, "v", 1) or 1) < 2:
continue

try:
if int(getattr(model, "v", 1) or 1) >= 2:
Expand Down Expand Up @@ -189,7 +193,7 @@ async def fetch_bids_for_start_block(

start_blk = await bittensor.block(start_block).get()
commits_raw = await subnet.commitments.fetch(block_hash=start_blk.hash)
bids_by_hotkey = parse_bidding_commitments(commits_raw)
bids_by_hotkey = parse_bidding_commitments(commits_raw, block_number=start_block)

state = getattr(getattr(bittensor, "subtensor", None), "state", None)
if state is not None and bids_by_hotkey:
Expand Down
7 changes: 7 additions & 0 deletions app/src/infinite_hashes/consensus/bidding.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
# Optional budget cap from validator commitments (defaults to no cap).
DEFAULT_BUDGET_CAP = 1.0
ILP_BIG_M_SWITCH_BLOCK = 7405572
V2_ONLY_SWITCH_BLOCK = 7414264 # v2-only activation (≈2h after block 7413664)


def v2_only_active(block_number: int | None) -> bool:
if block_number is None:
return False
return block_number >= V2_ONLY_SWITCH_BLOCK


class BiddingCommitment(CompactCommitment):
Expand Down
1 change: 1 addition & 0 deletions app/src/tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# Set Django settings for integration tests
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "infinite_hashes.settings")
os.environ.setdefault("PRICE_COMMITMENT_BUDGET_CAP", "1.0")
os.environ.setdefault("APS_MINER_ALLOW_V1", "1")


class _ProxyWorkersHandler(BaseHTTPRequestHandler):
Expand Down