-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Made Non-encrypted communication the default, but still support encryption - Few minor quality of life changes, nothing major
- Loading branch information
Showing
43 changed files
with
1,162 additions
and
109 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import os | ||
|
||
from dotenv import load_dotenv | ||
|
||
load_dotenv("dev.env") | ||
import asyncio | ||
|
||
import httpx | ||
from cryptography.fernet import Fernet | ||
|
||
from fiber.chain import chain_utils | ||
from fiber.logging_utils import get_logger | ||
from fiber.validator import client as vali_client | ||
from fiber.validator import handshake | ||
|
||
logger = get_logger(__name__) | ||
|
||
|
||
async def main(): | ||
# Load needed stuff | ||
wallet_name = os.getenv("WALLET_NAME", "default") | ||
hotkey_name = os.getenv("HOTKEY_NAME", "default") | ||
keypair = chain_utils.load_hotkey_keypair(wallet_name, hotkey_name) | ||
httpx_client = httpx.AsyncClient() | ||
|
||
# Handshake with miner | ||
miner_address = "http://localhost:7999" | ||
miner_hotkey_ss58_address = "5xyz_some_miner_hotkey" | ||
symmetric_key_str, symmetric_key_uuid = await handshake.perform_handshake( | ||
keypair=keypair, | ||
httpx_client=httpx_client, | ||
server_address=miner_address, | ||
miner_hotkey_ss58_address=miner_hotkey_ss58_address, | ||
) | ||
|
||
if symmetric_key_str is None or symmetric_key_uuid is None: | ||
raise ValueError("Symmetric key or UUID is None :-(") | ||
else: | ||
logger.info("Wohoo - handshake worked! :)") | ||
|
||
fernet = Fernet(symmetric_key_str) | ||
|
||
resp = await vali_client.make_non_streamed_post( | ||
httpx_client=httpx_client, | ||
server_address=miner_address, | ||
fernet=fernet, | ||
keypair=keypair, | ||
symmetric_key_uuid=symmetric_key_uuid, | ||
validator_ss58_address=keypair.ss58_address, | ||
miner_ss58_address=miner_hotkey_ss58_address, | ||
payload={}, | ||
endpoint="/example-subnet-request", | ||
) | ||
resp.raise_for_status() | ||
logger.info(f"Example request sent! Response: {resp.text}") | ||
|
||
|
||
if __name__ == "__main__": | ||
asyncio.run(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import os | ||
|
||
from dotenv import load_dotenv | ||
|
||
load_dotenv("dev.env") # Important to load this before importing anything else! | ||
|
||
from fiber.encrypted.miner import server | ||
from fiber.encrypted.miner.endpoints.subnet import factory_router as get_subnet_router | ||
from fiber.encrypted.miner.middleware import configure_extra_logging_middleware | ||
from fiber.logging_utils import get_logger | ||
|
||
logger = get_logger(__name__) | ||
|
||
app = server.factory_app(debug=True) | ||
|
||
app.include_router(get_subnet_router()) | ||
|
||
|
||
if os.getenv("ENV", "dev").lower() == "dev": | ||
configure_extra_logging_middleware(app) | ||
|
||
if __name__ == "__main__": | ||
import uvicorn | ||
|
||
uvicorn.run(app, host="127.0.0.1", port=7999) | ||
|
||
# Remember to fiber-post-ip to whatever testnet you are using! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"Just here to help testing" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import base64 | ||
import os | ||
from functools import lru_cache | ||
from typing import TypeVar | ||
|
||
import httpx | ||
from cryptography.hazmat.primitives import hashes | ||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC | ||
from dotenv import load_dotenv | ||
from pydantic import BaseModel | ||
|
||
from fiber.chain import chain_utils, interface | ||
from fiber.chain.metagraph import Metagraph | ||
from fiber.encrypted.miner.core import miner_constants as mcst | ||
from fiber.encrypted.miner.core.models.config import Config | ||
from fiber.encrypted.miner.security import key_management, nonce_management | ||
|
||
T = TypeVar("T", bound=BaseModel) | ||
|
||
load_dotenv() | ||
|
||
|
||
def _derive_key_from_string(input_string: str, salt: bytes = b"salt_") -> str: | ||
kdf = PBKDF2HMAC( | ||
algorithm=hashes.SHA256(), | ||
length=32, | ||
salt=salt, | ||
iterations=100000, | ||
) | ||
key = base64.urlsafe_b64encode(kdf.derive(input_string.encode())) | ||
return key.decode() | ||
|
||
|
||
@lru_cache | ||
def factory_config() -> Config: | ||
nonce_manager = nonce_management.NonceManager() | ||
|
||
wallet_name = os.getenv("WALLET_NAME", "default") | ||
hotkey_name = os.getenv("HOTKEY_NAME", "default") | ||
netuid = os.getenv("NETUID") | ||
subtensor_network = os.getenv("SUBTENSOR_NETWORK") | ||
subtensor_address = os.getenv("SUBTENSOR_ADDRESS") | ||
load_old_nodes = bool(os.getenv("LOAD_OLD_NODES", True)) | ||
min_stake_threshold = int(os.getenv("MIN_STAKE_THRESHOLD", 1_000)) | ||
refresh_nodes = os.getenv("REFRESH_NODES", "true").lower() == "true" | ||
|
||
assert netuid is not None, "Must set NETUID env var please!" | ||
|
||
if refresh_nodes: | ||
substrate = interface.get_substrate(subtensor_network, subtensor_address) | ||
metagraph = Metagraph( | ||
substrate=substrate, | ||
netuid=netuid, | ||
load_old_nodes=load_old_nodes, | ||
) | ||
else: | ||
metagraph = Metagraph(substrate=None, netuid=netuid, load_old_nodes=load_old_nodes) | ||
|
||
keypair = chain_utils.load_hotkey_keypair(wallet_name, hotkey_name) | ||
|
||
storage_encryption_key = os.getenv("STORAGE_ENCRYPTION_KEY") | ||
if storage_encryption_key is None: | ||
storage_encryption_key = _derive_key_from_string(mcst.DEFAULT_ENCRYPTION_STRING) | ||
|
||
encryption_keys_handler = key_management.EncryptionKeysHandler( | ||
nonce_manager, storage_encryption_key, hotkey=hotkey_name | ||
) | ||
|
||
return Config( | ||
encryption_keys_handler=encryption_keys_handler, | ||
keypair=keypair, | ||
metagraph=metagraph, | ||
min_stake_threshold=min_stake_threshold, | ||
httpx_client=httpx.AsyncClient(), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
SYMMETRIC_KEYS_FILENAME = "symmetric_keys.encrypted" | ||
DEFAULT_ENCRYPTION_STRING = "default_encryption" | ||
NONCE_WINDOW_NS = 120_000_000_000 # 2 minutes in nanoseconds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from dataclasses import dataclass | ||
|
||
import httpx | ||
from substrateinterface import Keypair | ||
|
||
from fiber.chain.metagraph import Metagraph | ||
from fiber.encrypted.miner.security import key_management | ||
|
||
|
||
@dataclass | ||
class Config: | ||
encryption_keys_handler: key_management.EncryptionKeysHandler | ||
keypair: Keypair | ||
metagraph: Metagraph | ||
min_stake_threshold: float | ||
httpx_client: httpx.AsyncClient |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from fastapi import Depends, Header, HTTPException | ||
|
||
from fiber import constants as cst | ||
from fiber.chain import signatures | ||
from fiber.encrypted import utils | ||
from fiber.encrypted.miner.core import configuration | ||
from fiber.encrypted.miner.core.models.config import Config | ||
from fiber.logging_utils import get_logger | ||
|
||
logger = get_logger(__name__) | ||
|
||
|
||
def get_config() -> Config: | ||
return configuration.factory_config() | ||
|
||
|
||
async def verify_request( | ||
validator_hotkey: str = Header(..., alias=cst.VALIDATOR_HOTKEY), | ||
signature: str = Header(..., alias=cst.SIGNATURE), | ||
miner_hotkey: str = Header(..., alias=cst.MINER_HOTKEY), | ||
nonce: str = Header(..., alias=cst.NONCE), | ||
symmetric_key_uuid: str = Header(..., alias=cst.SYMMETRIC_KEY_UUID), | ||
config: Config = Depends(get_config), | ||
): | ||
if not config.encryption_keys_handler.nonce_manager.nonce_is_valid(nonce): | ||
logger.debug("Nonce is not valid!") | ||
raise HTTPException( | ||
status_code=401, | ||
detail="Oi, that nonce is not valid!", | ||
) | ||
|
||
if not signatures.verify_signature( | ||
message=utils.construct_header_signing_message(nonce, miner_hotkey, symmetric_key_uuid), | ||
signer_ss58_address=validator_hotkey, | ||
signature=signature, | ||
): | ||
raise HTTPException( | ||
status_code=401, | ||
detail="Oi, invalid signature, you're not who you said you were!", | ||
) | ||
|
||
|
||
async def blacklist_low_stake( | ||
validator_hotkey: str = Header(..., alias=cst.VALIDATOR_HOTKEY), config: Config = Depends(get_config) | ||
): | ||
metagraph = config.metagraph | ||
|
||
node = metagraph.nodes.get(validator_hotkey) | ||
if not node: | ||
raise HTTPException(status_code=403, detail="Hotkey not found in metagraph") | ||
|
||
if node.stake < config.min_stake_threshold: | ||
logger.debug(f"Node {validator_hotkey} has insufficient stake of {node.stake} - minimum is {config.min_stake_threshold}") | ||
raise HTTPException(status_code=403, detail=f"Insufficient stake of {node.stake} ") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
""" | ||
THIS IS AN EXAMPLE FILE OF A SUBNET ENDPOINT! | ||
PLEASE IMPLEMENT YOUR OWN :) | ||
""" | ||
|
||
from functools import partial | ||
|
||
from fastapi import Depends | ||
from fastapi.routing import APIRouter | ||
from pydantic import BaseModel | ||
|
||
from fiber.encrypted.miner.dependencies import blacklist_low_stake, verify_request | ||
from fiber.encrypted.miner.security.encryption import decrypt_general_payload | ||
|
||
|
||
class ExampleSubnetRequest(BaseModel): | ||
pass | ||
|
||
|
||
async def example_subnet_request( | ||
decrypted_payload: ExampleSubnetRequest = Depends( | ||
partial(decrypt_general_payload, ExampleSubnetRequest), | ||
), | ||
): | ||
return {"status": "Example request received"} | ||
|
||
|
||
def factory_router() -> APIRouter: | ||
router = APIRouter() | ||
router.add_api_route( | ||
"/example-subnet-request", | ||
example_subnet_request, | ||
tags=["Example"], | ||
dependencies=[Depends(blacklist_low_stake), Depends(verify_request)], | ||
methods=["POST"], | ||
) | ||
return router |
Oops, something went wrong.