Skip to content

Commit

Permalink
Merge pull request #219 from backend-developers-ltd/local-miner-server
Browse files Browse the repository at this point in the history
Local miner server
  • Loading branch information
olzhasar-reef authored Aug 30, 2024
2 parents 5fa9cce + d85f195 commit 0e28455
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 103 deletions.
99 changes: 71 additions & 28 deletions install_miner.sh
Original file line number Diff line number Diff line change
@@ -1,50 +1,77 @@
#!/bin/bash
set -euxo pipefail

if [ $# -ne 2 ]
if [ $# -lt 3 ] || [ $# -gt 4 ];
then
>&2 echo "USAGE: ./install_miner.sh SSH_DESTINATION HOTKEY_PATH"
>&2 echo "USAGE: ./install_miner.sh MODE[production|local] SSH_DESTINATION HOTKEY_PATH|VALIDATOR_PUBLIC_KEY MINER_PORT:8000"
exit 1
fi

SSH_DESTINATION="$1"
LOCAL_HOTKEY_PATH=$(realpath "$2")
LOCAL_COLDKEY_PUB_PATH=$(dirname "$(dirname "$LOCAL_HOTKEY_PATH")")/coldkeypub.txt
MODE="$1"
SSH_DESTINATION="$2"

if [ ! -f "$LOCAL_HOTKEY_PATH" ]; then
>&2 echo "Given HOTKEY_PATH does not exist"
exit 1
fi
MINER_PORT="${4:-8000}"

if [ "$MODE" == "production" ]; then
LOCAL_HOTKEY_PATH=$(realpath "$3")
LOCAL_COLDKEY_PUB_PATH=$(dirname "$(dirname "$LOCAL_HOTKEY_PATH")")/coldkeypub.txt

if [ ! -f "$LOCAL_HOTKEY_PATH" ]; then
>&2 echo "Given HOTKEY_PATH does not exist"
exit 1
fi

HOTKEY_NAME=$(basename "$LOCAL_HOTKEY_PATH")
WALLET_NAME=$(basename "$(dirname "$(dirname "$LOCAL_HOTKEY_PATH")")")

# set default names if they contain special characters to avoid inconsistent behaviors by `.env` readers
[[ $HOTKEY_NAME =~ ['$#!;*?&()<>'\"\'] ]] && HOTKEY_NAME=default
[[ $WALLET_NAME =~ ['$#!;*?&()<>'\"\'] ]] && WALLET_NAME=mywallet

HOTKEY_NAME=$(basename "$LOCAL_HOTKEY_PATH")
WALLET_NAME=$(basename "$(dirname "$(dirname "$LOCAL_HOTKEY_PATH")")")
REMOTE_HOTKEY_PATH=".bittensor/wallets/$WALLET_NAME/hotkeys/$HOTKEY_NAME"
REMOTE_COLDKEY_PUB_PATH=".bittensor/wallets/$WALLET_NAME/coldkeypub.txt"
REMOTE_HOTKEY_DIR=$(dirname "$REMOTE_HOTKEY_PATH")

# set default names if they contain special characters to avoid inconsistent behaviors by `.env` readers
[[ $HOTKEY_NAME =~ ['$#!;*?&()<>'\"\'] ]] && HOTKEY_NAME=default
[[ $WALLET_NAME =~ ['$#!;*?&()<>'\"\'] ]] && WALLET_NAME=mywallet
REMOTE_HOTKEY_NAME="$(basename "$REMOTE_HOTKEY_PATH")"
REMOTE_WALLET_NAME="$(basename "$(dirname "$REMOTE_HOTKEY_DIR")")"

REMOTE_HOTKEY_PATH=".bittensor/wallets/$WALLET_NAME/hotkeys/$HOTKEY_NAME"
REMOTE_COLDKEY_PUB_PATH=".bittensor/wallets/$WALLET_NAME/coldkeypub.txt"
REMOTE_HOTKEY_DIR=$(dirname "$REMOTE_HOTKEY_PATH")
VALIDATOR_PUBLIC_KEY=""

elif [ "$MODE" == "local" ]; then
VALIDATOR_PUBLIC_KEY="$3"

REMOTE_HOTKEY_DIR=.bittensor/wallets/dummy/hotkeys
REMOTE_HOTKEY_NAME=dummy
REMOTE_WALLET_NAME=dummy
else
>&2 echo "Invalid mode $MODE. Must be 'production' or 'local'"
exit 1
fi

DEFAULT_ADMIN_PASSWORD=$(python3 -c 'import secrets; print(secrets.token_urlsafe(25))')
: "${MIGRATING:=0}"

# Copy the wallet files to the server
# shellcheck disable=SC2087
# Create tmpvars file on the server
ssh "$SSH_DESTINATION" <<ENDSSH
set -euxo pipefail
mkdir -p $REMOTE_HOTKEY_DIR
cat > tmpvars <<ENDCAT
HOTKEY_NAME="$(basename "$REMOTE_HOTKEY_PATH")"
WALLET_NAME="$(basename "$(dirname "$REMOTE_HOTKEY_DIR")")"
HOTKEY_NAME=$REMOTE_HOTKEY_NAME
WALLET_NAME=$REMOTE_WALLET_NAME
DEFAULT_ADMIN_PASSWORD="$DEFAULT_ADMIN_PASSWORD"
MIGRATING=$MIGRATING
MINER_PORT=$MINER_PORT
VALIDATOR_PUBLIC_KEY=$VALIDATOR_PUBLIC_KEY
ENDCAT
ENDSSH
scp "$LOCAL_HOTKEY_PATH" "$SSH_DESTINATION:$REMOTE_HOTKEY_PATH"
scp "$LOCAL_COLDKEY_PUB_PATH" "$SSH_DESTINATION:$REMOTE_COLDKEY_PUB_PATH"

if [ "$MODE" == "production" ]; then
# Copy the wallet files to the server
# shellcheck disable=SC2087
scp "$LOCAL_HOTKEY_PATH" "$SSH_DESTINATION:$REMOTE_HOTKEY_PATH"
scp "$LOCAL_COLDKEY_PUB_PATH" "$SSH_DESTINATION:$REMOTE_COLDKEY_PUB_PATH"
fi

# install necessary software in the server
ssh "$SSH_DESTINATION" <<'ENDSSH'
Expand Down Expand Up @@ -133,34 +160,50 @@ BITTENSOR_NETWORK=finney
BITTENSOR_WALLET_NAME="$(. ~/tmpvars && echo "$WALLET_NAME")"
BITTENSOR_WALLET_HOTKEY_NAME="$(. ~/tmpvars && echo "$HOTKEY_NAME")"
HOST_WALLET_DIR=$HOME/.bittensor/wallets
# for now, PORT_FOR_EXECUTORS has to be the same as BITTENSOR_MINER_PORT, unless you change nginx configuration yourself (we don't advise doing that)
BITTENSOR_MINER_PORT=8000
BITTENSOR_MINER_PORT=$(. ~/tmpvars && echo "$MINER_PORT")
BITTENSOR_MINER_ADDRESS=auto
COMPOSE_PROJECT_NAME=compute_horde_miner
# make sure to unblock access to that port in your firewall
PORT_FOR_EXECUTORS=8000
PORT_FOR_EXECUTORS=$(. ~/tmpvars && echo "$MINER_PORT")
ADDRESS_FOR_EXECUTORS=172.17.0.1
DEFAULT_ADMIN_PASSWORD="$(. ~/tmpvars && echo "$DEFAULT_ADMIN_PASSWORD")"
MIGRATING="$(. ~/tmpvars && echo "$MIGRATING")"
ENDENV
ENDSSH

rm ~/tmpvars
if [ "$MODE" == "local" ]; then
ssh "$SSH_DESTINATION" <<'ENDSSH'
set -euxo pipefail
cat >> ~/compute_horde_miner/.env <<ENDENV
IS_LOCAL_MINER=1
LOCAL_MINER_VALIDATOR_PUBLIC_KEY="$(. ~/tmpvars && echo "$VALIDATOR_PUBLIC_KEY")"
ENDENV
ENDSSH
fi

ssh "$SSH_DESTINATION" <<ENDSSH
set -euxo pipefail
cd ~/compute_horde_miner
docker pull backenddevelopersltd/compute-horde-executor:v0-latest
docker pull backenddevelopersltd/compute-horde-miner:v0-latest
docker pull backenddevelopersltd/compute-horde-job:v0-latest
docker compose up -d
rm ~/tmpvars
ENDSSH

set +x
MINER_HOSTNAME=$(ssh -G "$SSH_DESTINATION" | grep '^hostname' | cut -d' ' -f2)
MINER_ADMIN_LOGIN_URL="http://$MINER_HOSTNAME:8000/admin/login/"
MINER_ADMIN_LOGIN_URL="http://$MINER_HOSTNAME:$MINER_PORT/admin/login/"

for run in {1..10}
do
Expand Down Expand Up @@ -203,5 +246,5 @@ EOF
done

>&2 echo "Cannot connect to miner. Please check if everything is installed and running properly."
>&2 echo "Also make sure port 8000 is reachable from outside (i.e. not blocked in firewall)."
>&2 echo "Also make sure port $MINER_PORT is reachable from outside (i.e. not blocked in firewall)."
exit 1
5 changes: 4 additions & 1 deletion miner/app/src/compute_horde_miner/miner/admin_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
class MinerAdminSite(AdminSite):
def each_context(self, request):
context = super().each_context(request)
context["hotkey"] = settings.BITTENSOR_WALLET().hotkey.ss58_address
context["netuid"] = settings.BITTENSOR_NETUID
if not settings.IS_LOCAL_MINER:
context["hotkey"] = settings.BITTENSOR_WALLET().hotkey.ss58_address
else:
context["hotkey"] = "local"
return context
19 changes: 19 additions & 0 deletions miner/app/src/compute_horde_miner/miner/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,27 @@ def maybe_create_default_admin(sender, **kwargs):
)


def create_local_miner_validator(sender, **kwargs):
if not settings.IS_LOCAL_MINER:
return

assert (
settings.LOCAL_MINER_VALIDATOR_PUBLIC_KEY
), "LOCAL_MINER_VALIDATOR_PUBLIC_KEY needs to be set to run miner in local mode"

from .models import Validator

instance, created = Validator.objects.get_or_create(
public_key=settings.LOCAL_MINER_VALIDATOR_PUBLIC_KEY,
defaults={"active": True, "debug": False},
)
if created:
logger.info("Created validator with public key %s", instance.public_key)


class MinerConfig(AppConfig):
name = "compute_horde_miner.miner"

def ready(self):
post_migrate.connect(maybe_create_default_admin, sender=self)
post_migrate.connect(create_local_miner_validator, sender=self)
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_miner_signature(msg: BaseValidatorRequest) -> str:
class MinerValidatorConsumer(BaseConsumer, ValidatorInterfaceMixin):
def __init__(self, *a, **kw):
super().__init__(*a, **kw)
if settings.DEBUG_TURN_AUTHENTICATION_OFF:
if settings.DEBUG_TURN_AUTHENTICATION_OFF or settings.IS_LOCAL_MINER:
self.my_hotkey = DONT_CHECK
else:
self.my_hotkey = settings.BITTENSOR_WALLET().get_hotkey().ss58_address
Expand Down Expand Up @@ -125,7 +125,7 @@ def outgoing_generic_error_class(self):
def verify_auth_msg(self, msg: validator_requests.V0AuthenticateRequest) -> tuple[bool, str]:
if msg.payload.timestamp < time.time() - AUTH_MESSAGE_MAX_AGE:
return False, "msg too old"
if msg.payload.miner_hotkey != self.my_hotkey:
if not settings.IS_LOCAL_MINER and msg.payload.miner_hotkey != self.my_hotkey:
return False, f"wrong miner hotkey ({self.my_hotkey}!={msg.payload.miner_hotkey})"
if msg.payload.validator_hotkey != self.validator_key:
return (
Expand All @@ -144,6 +144,9 @@ def verify_receipt_msg(
msg: validator_requests.V0JobStartedReceiptRequest
| validator_requests.V0JobFinishedReceiptRequest,
) -> bool:
if settings.IS_LOCAL_MINER:
return True

if self.my_hotkey != DONT_CHECK and msg.payload.miner_hotkey != self.my_hotkey:
logger.warning(
f"Miner hotkey mismatch in receipt for job_uuid {msg.payload.job_uuid} ({msg.payload.miner_hotkey!r} != {self.my_hotkey!r})"
Expand Down Expand Up @@ -321,6 +324,10 @@ async def handle(self, msg: BaseValidatorRequest):
f" job_uuid={msg.payload.job_uuid} validator_hotkey={msg.payload.validator_hotkey}"
f" max_timeout={msg.payload.max_timeout}"
)

if settings.IS_LOCAL_MINER:
return

await JobStartedReceipt.objects.acreate(
validator_signature=msg.signature,
miner_signature=get_miner_signature(msg),
Expand All @@ -345,6 +352,9 @@ async def handle(self, msg: BaseValidatorRequest):
job.score = msg.payload.score
await job.asave()

if settings.IS_LOCAL_MINER:
return

await JobFinishedReceipt.objects.acreate(
validator_signature=msg.signature,
miner_signature=get_miner_signature(msg),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async def fake_executor(token):
fake_executor.job_uuid = None


class TestExecutorManager(v1.BaseExecutorManager):
class StubExecutorManager(v1.BaseExecutorManager):
async def start_new_executor(self, token, executor_class, timeout):
asyncio.get_running_loop().create_task(fake_executor(token))
return object()
Expand Down
Loading

0 comments on commit 0e28455

Please sign in to comment.