From 0a5c06e3126e277ce21d0d2fa43574717594008f Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Thu, 13 Feb 2025 18:51:42 -0300 Subject: [PATCH] First version integrated to rust contract --- .github/workflows/ci.yml | 45 ++- .gitmodules | 3 + skynet/_testing.py | 3 +- skynet/constants.py | 218 -------------- skynet/contract.py | 304 ++++++++++++++++++++ skynet/dgpu/daemon.py | 25 +- skynet/dgpu/network.py | 192 +------------ skynet/types.py | 72 +++++ tests/conftest.py | 13 +- tests/contracts/gpu.scd/gpu.scd.abi | 416 --------------------------- tests/contracts/gpu.scd/gpu.scd.wasm | Bin 46064 -> 0 bytes tests/contracts/skygpu-contract | 1 + tests/test_chain.py | 222 +++++++++++++- uv.lock | 31 +- 14 files changed, 680 insertions(+), 865 deletions(-) create mode 100644 .gitmodules create mode 100644 skynet/contract.py delete mode 100644 tests/contracts/gpu.scd/gpu.scd.abi delete mode 100755 tests/contracts/gpu.scd/gpu.scd.wasm create mode 160000 tests/contracts/skygpu-contract diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 607a122..d5f29a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,14 +7,57 @@ jobs: name: Pytest Tests runs-on: ubuntu-24.04 timeout-minutes: 10 + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive + - name: Install system dependencies (Rust + Python) + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + pkg-config \ + libssl-dev \ + clang \ + lld \ + protobuf-compiler \ + make \ + wget + + - name: Install binaryen 120 + run: | + wget https://github.com/WebAssembly/binaryen/releases/download/version_120/binaryen-version_120-x86_64-linux.tar.gz + tar xvf binaryen-version_120-x86_64-linux.tar.gz + echo "$(pwd)/binaryen-version_120/bin" >> $GITHUB_PATH + - name: Install the latest version of uv uses: astral-sh/setup-uv@v5 + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + rustup install stable + rustup component add rust-src --toolchain stable + rustup target add wasm32-wasip1 + + - name: Set up Python environment and dependencies + run: | + uv venv .venv --python=3.12 + uv pip install -U rust-contracts-builder + echo "$(pwd)/.venv/bin" >> $GITHUB_PATH + + - name: Apply required modifications for rust-contracts-builder + run: | + sed -i "s/wasm32-wasi /wasm32-wasip1 /g" .venv/lib/python3.12/site-packages/rust_contracts_builder/__init__.py + sed -i "s/wasm32-wasi\//wasm32-wasip1\//g" .venv/lib/python3.12/site-packages/rust_contracts_builder/__init__.py + + - name: Build Rust project + run: rust-contract build + working-directory: tests/contracts/skygpu-contract + - uses: actions/cache@v3 name: Cache venv with: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..90b8d3e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/contracts/skygpu-contract"] + path = tests/contracts/skygpu-contract + url = https://github.com/skygpu/skygpu-contract.git diff --git a/skynet/_testing.py b/skynet/_testing.py index 44eefba..fc599c5 100644 --- a/skynet/_testing.py +++ b/skynet/_testing.py @@ -16,13 +16,14 @@ async def open_test_worker( cleos, ipfs_node, account: str = 'testworker', permission: str = 'active', + key: str = '5KRPFxF4RJebqPXqRzwStmCaEWeRfp3pR7XUNoA3zCHt5fnPu3s', hf_token: str = '', **kwargs ): config = override_dgpu_config( account=account, permission=permission, - key=cleos.private_keys[account], + key=key, node_url=cleos.endpoint, ipfs_url=ipfs_node[1].endpoint, hf_token=hf_token, diff --git a/skynet/constants.py b/skynet/constants.py index 4de9b23..b5d255c 100755 --- a/skynet/constants.py +++ b/skynet/constants.py @@ -271,221 +271,3 @@ TG_MAX_WIDTH = 1280 TG_MAX_HEIGHT = 1280 DEFAULT_SINGLE_CARD_MAP = 'cuda:0' - -GPU_CONTRACT_ABI = { - "version": "eosio::abi/1.2", - "types": [], - "structs": [ - { - "name": "account", - "base": "", - "fields": [ - {"name": "user", "type": "name"}, - {"name": "balance", "type": "asset"}, - {"name": "nonce", "type": "uint64"} - ] - }, - { - "name": "card", - "base": "", - "fields": [ - {"name": "id", "type": "uint64"}, - {"name": "owner", "type": "name"}, - {"name": "card_name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "total_memory", "type": "uint64"}, - {"name": "mp_count", "type": "uint32"}, - {"name": "extra", "type": "string"} - ] - }, - { - "name": "clean", - "base": "", - "fields": [] - }, - { - "name": "config", - "base": "", - "fields": [ - {"name": "token_contract", "type": "name"}, - {"name": "token_symbol", "type": "symbol"} - ] - }, - { - "name": "dequeue", - "base": "", - "fields": [ - {"name": "user", "type": "name"}, - {"name": "request_id", "type": "uint64"} - ] - }, - { - "name": "enqueue", - "base": "", - "fields": [ - {"name": "user", "type": "name"}, - {"name": "request_body", "type": "string"}, - {"name": "binary_data", "type": "string"}, - {"name": "reward", "type": "asset"}, - {"name": "min_verification", "type": "uint32"} - ] - }, - { - "name": "gcfgstruct", - "base": "", - "fields": [ - {"name": "token_contract", "type": "name"}, - {"name": "token_symbol", "type": "symbol"} - ] - }, - { - "name": "submit", - "base": "", - "fields": [ - {"name": "worker", "type": "name"}, - {"name": "request_id", "type": "uint64"}, - {"name": "request_hash", "type": "checksum256"}, - {"name": "result_hash", "type": "checksum256"}, - {"name": "ipfs_hash", "type": "string"} - ] - }, - { - "name": "withdraw", - "base": "", - "fields": [ - {"name": "user", "type": "name"}, - {"name": "quantity", "type": "asset"} - ] - }, - { - "name": "work_request_struct", - "base": "", - "fields": [ - {"name": "id", "type": "uint64"}, - {"name": "user", "type": "name"}, - {"name": "reward", "type": "asset"}, - {"name": "min_verification", "type": "uint32"}, - {"name": "nonce", "type": "uint64"}, - {"name": "body", "type": "string"}, - {"name": "binary_data", "type": "string"}, - {"name": "timestamp", "type": "time_point_sec"} - ] - }, - { - "name": "work_result_struct", - "base": "", - "fields": [ - {"name": "id", "type": "uint64"}, - {"name": "request_id", "type": "uint64"}, - {"name": "user", "type": "name"}, - {"name": "worker", "type": "name"}, - {"name": "result_hash", "type": "checksum256"}, - {"name": "ipfs_hash", "type": "string"}, - {"name": "submited", "type": "time_point_sec"} - ] - }, - { - "name": "workbegin", - "base": "", - "fields": [ - {"name": "worker", "type": "name"}, - {"name": "request_id", "type": "uint64"}, - {"name": "max_workers", "type": "uint32"} - ] - }, - { - "name": "workcancel", - "base": "", - "fields": [ - {"name": "worker", "type": "name"}, - {"name": "request_id", "type": "uint64"}, - {"name": "reason", "type": "string"} - ] - }, - { - "name": "worker", - "base": "", - "fields": [ - {"name": "account", "type": "name"}, - {"name": "joined", "type": "time_point_sec"}, - {"name": "left", "type": "time_point_sec"}, - {"name": "url", "type": "string"} - ] - }, - { - "name": "worker_status_struct", - "base": "", - "fields": [ - {"name": "worker", "type": "name"}, - {"name": "status", "type": "string"}, - {"name": "started", "type": "time_point_sec"} - ] - } - ], - "actions": [ - {"name": "clean", "type": "clean", "ricardian_contract": ""}, - {"name": "config", "type": "config", "ricardian_contract": ""}, - {"name": "dequeue", "type": "dequeue", "ricardian_contract": ""}, - {"name": "enqueue", "type": "enqueue", "ricardian_contract": ""}, - {"name": "submit", "type": "submit", "ricardian_contract": ""}, - {"name": "withdraw", "type": "withdraw", "ricardian_contract": ""}, - {"name": "workbegin", "type": "workbegin", "ricardian_contract": ""}, - {"name": "workcancel", "type": "workcancel", "ricardian_contract": ""} - ], - "tables": [ - { - "name": "cards", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "card" - }, - { - "name": "gcfgstruct", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "gcfgstruct" - }, - { - "name": "queue", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "work_request_struct" - }, - { - "name": "results", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "work_result_struct" - }, - { - "name": "status", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "worker_status_struct" - }, - { - "name": "users", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "account" - }, - { - "name": "workers", - "index_type": "i64", - "key_names": [], - "key_types": [], - "type": "worker" - } - ], - "ricardian_clauses": [], - "error_messages": [], - "abi_extensions": [], - "variants": [], - "action_results": [] -} diff --git a/skynet/contract.py b/skynet/contract.py new file mode 100644 index 0000000..11e0412 --- /dev/null +++ b/skynet/contract.py @@ -0,0 +1,304 @@ +import time + +import msgspec +from leap import CLEOS +from leap.protocol import Name + +from skynet.types import ( + ConfigV1, + AccountV1, + WorkerV0, + RequestV1, + BodyV0, + WorkerStatusV0, + ResultV0 +) + + +class ConfigNotFound(BaseException): + ... + +class AccountNotFound(BaseException): + ... + +class WorkerNotFound(BaseException): + ... + +class RequestNotFound(BaseException): + ... + +class WorkerStatusNotFound(BaseException): + ... + + +class GPUContractAPI: + + def __init__(self, cleos: CLEOS): + self.receiver = 'gpu.scd' + self._cleos = cleos + + # views into data + + async def get_config(self) -> ConfigV1: + rows = await self._cleos.aget_table( + self.receiver, self.receiver, 'config', + resp_cls=ConfigV1 + ) + if len(rows) == 0: + raise ConfigNotFound() + + return rows[0] + + async def get_user(self, user: str) -> AccountV1: + rows = await self._cleos.aget_table( + self.receiver, self.receiver, 'users', + key_type='name', + lower_bound=user, + upper_bound=user, + resp_cls=AccountV1 + ) + if len(rows) == 0: + raise AccountNotFound(user) + + return rows[0] + + async def get_users(self) -> list[AccountV1]: + return await self._cleos.aget_table(self.receiver, self.receiver, 'users', resp_cls=AccountV1) + + async def get_worker(self, worker: str) -> WorkerV0: + rows = await self._cleos.aget_table( + self.receiver, self.receiver, 'workers', + key_type='name', + lower_bound=worker, + upper_bound=worker, + resp_cls=WorkerV0 + ) + if len(rows) == 0: + raise WorkerNotFound(worker) + + return rows[0] + + async def get_workers(self) -> list[AccountV1]: + return await self._cleos.aget_table(self.receiver, self.receiver, 'workers', resp_cls=WorkerV0) + + async def get_queue(self) -> RequestV1: + return await self._cleos.aget_table(self.receiver, self.receiver, 'queue', resp_cls=RequestV1) + + async def get_request(self, request_id: int) -> RequestV1: + rows = await self._cleos.aget_table( + self.receiver, self.receiver, 'queue', + lower_bound=request_id, + upper_bound=request_id, + resp_cls=RequestV1 + ) + if len(rows) == 0: + raise RequestNotFound(request_id) + + return rows[0] + + async def get_requests_since(self, seconds: int) -> list[RequestV1]: + return await self._cleos.aget_table( + self.receiver, self.receiver, 'queue', + index_position=2, + key_type='i64', + lower_bound=int(time.time()) - seconds, + resp_cls=RequestV1 + ) + + async def get_statuses_for_request(self, request_id: int) -> list[WorkerStatusV0]: + return await self._cleos.aget_table( + self.receiver, str(Name.from_int(request_id)), 'status', + resp_cls=WorkerStatusV0 + ) + + async def get_worker_status_for_request(self, request_id: int, worker: str) -> WorkerStatusV0: + rows = await self._cleos.aget_table( + self.receiver, str(Name.from_int(request_id)), 'status', + key_type='name', + lower_bound=worker, + upper_bound=worker, + resp_cls=WorkerStatusV0 + ) + if len(rows) == 0: + raise WorkerStatusNotFound(request_id) + + return rows[0] + + async def get_results(self, request_id: int) -> list[ResultV0]: + return await self._cleos.aget_table( + self.receiver, self.receiver, 'results', + index_position=2, + key_type='i64', + lower_bound=request_id, + upper_bound=request_id, + resp_cls=ResultV0 + ) + + async def get_worker_results(self, worker: str) -> list[ResultV0]: + return await self._cleos.aget_table( + self.receiver, self.receiver, 'results', + index_position=4, + key_type='name', + lower_bound=worker, + upper_bound=worker, + resp_cls=ResultV0 + ) + + # system actions + async def init_config(self, token_account: str, token_symbol: str): + return await self._cleos.a_push_action( + self.receiver, + 'config', + [token_account, token_symbol], + self.receiver + ) + + async def clean_tables(self, nuke: bool = False): + return await self._cleos.a_push_action( + self.receiver, + 'clean', + [nuke], + self.receiver + ) + + # balance actions + + async def deposit(self, user: str, quantity: str): + return await self._cleos.a_push_action( + 'eosio.token', + 'transfer', + [user, self.receiver, quantity, 'testing gpu deposit'], + user, + key=self._cleos.private_keys[user] + ) + + async def withdraw(self, user: str, quantity: str): + return await self._cleos.a_push_action( + self.receiver, + 'withdraw', + [user, quantity], + user, + key=self._cleos.private_keys[user] + ) + + # worker actions + + async def register_worker( + self, + worker: str, + url: str + ): + return await self._cleos.a_push_action( + self.receiver, + 'regworker', + [worker, url], + worker, + key=self._cleos.private_keys[worker] + ) + + async def unregister_worker( + self, + worker: str, + reason: str + ): + return await self._cleos.a_push_action( + self.receiver, + 'unregworker', + [worker, reason], + worker, + key=self._cleos.private_keys[worker] + ) + + async def accept_work( + self, + worker: str, + request_id: int, + max_workers: int = 10 + ): + return await self._cleos.a_push_action( + self.receiver, + 'workbegin', + [worker, request_id, max_workers], + worker, + key=self._cleos.private_keys[worker] + ) + + async def cancel_work( + self, + worker: str, + request_id: int, + reason: str + ): + return await self._cleos.a_push_action( + self.receiver, + 'workcancel', + [worker, request_id, reason], + worker, + key=self._cleos.private_keys[worker] + ) + + async def submit_work( + self, + worker: str, + request_id: int, + result_hash: str, + ipfs_hash: str + ): + return await self._cleos.a_push_action( + self.receiver, + 'submit', + [worker, request_id, result_hash, ipfs_hash], + worker, + key=self._cleos.private_keys[worker] + ) + + # user actions + + async def enqueue( + self, + account: str, + body: BodyV0, + binary_data: str = '', + reward: str = '1.0000 TLOS', + min_verification: int = 1 + ) -> int: + + body = msgspec.json.encode(body).decode('utf-8') + result = await self._cleos.a_push_action( + self.receiver, + 'enqueue', + [ + account, + body, + binary_data, + reward, + min_verification + ], + account, + key=self._cleos.private_keys[account] + ) + console = result['processed']['action_traces'][0]['console'] + nonce_index = -1 + timestamp_index = -2 + lines = console.rstrip().split('\n') + nonce = int(lines[nonce_index]) + timestamp = lines[timestamp_index] + + return RequestV1( + id=int(nonce), + user=account, + reward=reward, + min_verification=min_verification, + body=body, + binary_data=binary_data, + timestamp=timestamp + ) + + async def dequeue(self, user: str, request_id: int): + return await self._cleos.a_push_action( + self.receiver, + 'dequeue', + [user, request_id], + user, + key=self._cleos.private_keys[user] + ) diff --git a/skynet/dgpu/daemon.py b/skynet/dgpu/daemon.py index 2b47069..2d09d3e 100755 --- a/skynet/dgpu/daemon.py +++ b/skynet/dgpu/daemon.py @@ -1,6 +1,5 @@ import logging from functools import partial -from hashlib import sha256 import trio import msgspec @@ -22,7 +21,7 @@ from skynet.dgpu.network import ( async def maybe_update_tui_balance(conn: NetConnector): async def _fn(tui): # update balance - balance = await conn.get_worker_balance() + balance = await conn.contract.get_user(tui.config.account).balance tui.set_header_text(new_balance=f'balance: {balance}') await maybe_update_tui_async(_fn) @@ -101,24 +100,12 @@ async def maybe_serve_one( f'IPFS fetch input error !?! retries left {retry - r - 1}\n' ) - # compute unique request hash used on submit - hash_str = ( - str(req.nonce) - + - req.body - + - req.binary_data - ) - logging.debug(f'hashing: {hash_str}') - request_hash = sha256(hash_str.encode('utf-8')).hexdigest() - logging.info(f'calculated request hash: {request_hash}') - total_step = body.params.step mode = body.method # TODO: validate request - resp = await conn.begin_work(req.id) + resp = await conn.contract.accept_work(config.account, req.id) if not resp or 'code' in resp: logging.info('begin_work error, probably being worked on already... skip.') return @@ -157,7 +144,9 @@ async def maybe_serve_one( ipfs_hash = await conn.publish_on_ipfs(output, typ=output_type) - await conn.submit_work(req.id, request_hash, output_hash, ipfs_hash) + await conn.contract.submit_work(config.account, req.id, output_hash, ipfs_hash) + + await state_mngr.update_state() await maybe_update_tui_balance(conn) @@ -168,8 +157,10 @@ async def maybe_serve_one( if 'network cancel' not in str(err): logging.exception('Failed to serve model request !?\n') + await state_mngr.update_state() + if state_mngr.is_request_in_progress(req.id): - await conn.cancel_work(req.id, 'reason not provided') + await conn.contract.cancel_work(config.account, req.id, 'reason not provided') async def dgpu_serve_forever( diff --git a/skynet/dgpu/network.py b/skynet/dgpu/network.py index 6a8350b..cc6add5 100755 --- a/skynet/dgpu/network.py +++ b/skynet/dgpu/network.py @@ -15,18 +15,15 @@ import outcome import msgspec from PIL import Image from leap.cleos import CLEOS -from leap.protocol import Asset from skynet.dgpu.tui import maybe_update_tui from skynet.config import DgpuConfig as Config, load_skynet_toml from skynet.types import ( - ConfigV0, - AccountV0, BodyV0, - RequestV0, + RequestV1, WorkerStatusV0, ResultV0 ) -from skynet.constants import GPU_CONTRACT_ABI +from skynet.contract import GPUContractAPI from skynet.ipfs import ( AsyncIPFSHTTP, @@ -70,178 +67,16 @@ class NetConnector: def __init__(self, config: Config): self.config = config self.cleos = CLEOS(endpoint=config.node_url) - self.cleos.load_abi('gpu.scd', GPU_CONTRACT_ABI) + self.cleos.import_key(config.account, config.key) + abi = self.cleos.get_abi('gpu.scd') + self.cleos.load_abi('gpu.scd', abi) + + self.contract = GPUContractAPI(self.cleos) self.ipfs_client = AsyncIPFSHTTP(config.ipfs_url) maybe_update_tui(lambda tui: tui.set_header_text(new_worker_name=self.config.account)) - - # blockchain helpers - - async def get_work_requests_last_hour(self) -> list[RequestV0]: - logging.info('get_work_requests_last_hour') - rows = await failable( - partial( - self.cleos.aget_table, - 'gpu.scd', 'gpu.scd', 'queue', - index_position=2, - key_type='i64', - lower_bound=int(time.time()) - 3600, - resp_cls=RequestV0 - ), ret_fail=[]) - - logging.info(f'found {len(rows)} requests on queue') - return rows - - async def get_status_by_request_id(self, request_id: int) -> list[WorkerStatusV0]: - logging.info('get_status_by_request_id') - rows = await failable( - partial( - self.cleos.aget_table, - 'gpu.scd', request_id, 'status', resp_cls=WorkerStatusV0), ret_fail=[]) - - logging.info(f'found status for workers: {[r.worker for r in rows]}') - return rows - - async def get_global_config(self) -> ConfigV0: - logging.info('get_global_config') - rows = await failable( - partial( - self.cleos.aget_table, - 'gpu.scd', 'gpu.scd', 'config', - resp_cls=ConfigV0)) - - if rows: - cfg = rows[0] - logging.info(f'config found: {cfg}') - return cfg - else: - logging.error('global config not found, is the contract initialized?') - return None - - async def get_worker_balance(self) -> str: - logging.info('get_worker_balance') - rows = await failable( - partial( - self.cleos.aget_table, - 'gpu.scd', 'gpu.scd', 'users', - index_position=1, - key_type='name', - lower_bound=self.config.account, - upper_bound=self.config.account, - resp_cls=AccountV0 - )) - - if rows: - b = rows[0].balance - logging.info(f'balance: {b}') - return b - else: - logging.info('no balance info found') - return None - - async def begin_work(self, request_id: int): - ''' - Publish to the bc that the worker is beginning a model-computation - step. - - ''' - logging.info(f'begin_work on #{request_id}') - return await failable( - partial( - self.cleos.a_push_action, - 'gpu.scd', - 'workbegin', - list({ - 'worker': self.config.account, - 'request_id': request_id, - 'max_workers': 2 - }.values()), - self.config.account, self.config.key, - permission=self.config.permission - ) - ) - - async def cancel_work(self, request_id: int, reason: str): - logging.info(f'cancel_work on #{request_id}') - return await failable( - partial( - self.cleos.a_push_action, - 'gpu.scd', - 'workcancel', - list({ - 'worker': self.config.account, - 'request_id': request_id, - 'reason': reason - }.values()), - self.config.account, self.config.key, - permission=self.config.permission - ) - ) - - async def maybe_withdraw_all(self): - logging.info('maybe_withdraw_all') - balance = await self.get_worker_balance() - if not balance: - return - - balance_amount = float(balance.split(' ')[0]) - if balance_amount > 0: - await failable( - partial( - self.cleos.a_push_action, - 'gpu.scd', - 'withdraw', - list({ - 'user': self.config.account, - 'quantity': Asset.from_str(balance) - }.values()), - self.config.account, self.config.key, - permission=self.config.permission - ) - ) - - async def find_results(self) -> list[ResultV0]: - logging.info('find_results') - rows = await failable( - partial( - self.cleos.aget_table, - 'gpu.scd', 'gpu.scd', 'results', - index_position=4, - key_type='name', - lower_bound=self.config.account, - upper_bound=self.config.account, - resp_cls=ResultV0 - ) - ) - return rows - - async def submit_work( - self, - request_id: int, - request_hash: str, - result_hash: str, - ipfs_hash: str - ): - logging.info(f'submit_work #{request_id}') - return await failable( - partial( - self.cleos.a_push_action, - 'gpu.scd', - 'submit', - list({ - 'worker': self.config.account, - 'request_id': request_id, - 'request_hash': request_hash, - 'result_hash': result_hash, - 'ipfs_hash': ipfs_hash - }.values()), - self.config.account, self.config.key, - permission=self.config.permission - ) - ) - # IPFS helpers async def publish_on_ipfs(self, raw, typ: str = 'png'): Path('ipfs-staging').mkdir(exist_ok=True) @@ -302,9 +137,10 @@ class ContractState: def __init__(self, conn: NetConnector): self._conn = conn + self._config = load_skynet_toml().dgpu self._poll_index = 0 - self._queue: list[RequestV0] = [] + self._queue: list[RequestV1] = [] self._status_by_rid: dict[int, list[WorkerStatusV0]] = {} self._results: list[ResultV0] = [] @@ -315,10 +151,10 @@ class ContractState: return self._poll_index async def _fetch_results(self): - self._results = await self._conn.find_results() + self._results = await self._conn.contract.get_worker_results(self._config.account) async def _fetch_statuses_for_id(self, rid: int): - self._status_by_rid[rid] = await self._conn.get_status_by_request_id(rid) + self._status_by_rid[rid] = await self._conn.contract.get_statuses_for_request(rid) async def update_state(self): ''' @@ -326,7 +162,7 @@ class ContractState: ''' # raw queue from chain - _queue = await self._conn.get_work_requests_last_hour() + _queue = await self._conn.contract.get_requests_since(3600) # filter out invalids self._queue = [] @@ -380,7 +216,7 @@ class ContractState: return len(self._queue) @property - def first(self) -> RequestV0 | None: + def first(self) -> RequestV1 | None: if len(self._queue) > 0: return self._queue[0] @@ -391,7 +227,7 @@ class ContractState: return set(( status.worker for status in self._status_by_rid[request_id] - if status.worker != self._conn.config.account + if status.worker != self._config.account )) # predicates diff --git a/skynet/types.py b/skynet/types.py index 4e7c3a0..4b44c1d 100644 --- a/skynet/types.py +++ b/skynet/types.py @@ -39,6 +39,25 @@ class ConfigV0: token_contract: str token_symbol: str +''' +ConfigV1 + +singleton containing global info about system, definition: +```rust +#[chain(table="config", singleton)] +pub struct Config { + token_account: Name, + token_symbol: Symbol, + global_nonce: u64 +} +``` +''' + +class ConfigV1(Struct): + token_account: str + token_symbol: str + global_nonce: int + ''' RequestV0 @@ -103,6 +122,38 @@ class RequestV0(Struct): binary_data: str timestamp: str +''' +RequestV1 + +a request placed on the queue, definition: +NEW: nonce field removed + +scope: self.receiver + +```rust +#[chain(table="queue")] +pub struct Request { + #[chain(primary)] + id: u64, + user: Name, + reward: Asset, + min_verification: u32, + body: String, + binary_data: String, + #[chain(secondary)] + timestamp: TimePointSec +} +``` +''' +class RequestV1(Struct): + id: int + user: str + reward: str + min_verification: int + body: str + binary_data: str + timestamp: str + ''' AccountV0 @@ -128,6 +179,27 @@ class AccountV0(Struct): balance: str nonce: int +''' +AccountV1 + +a user account, users must deposit tokens in order to enqueue requests, definition: + +scope: self.receiver + +```rust +#[chain(table="users")] +pub struct Account { + #[chain(primary)] + user: Name, + balance: Asset +} +``` +''' + +class AccountV1(Struct): + user: str + balance: str + ''' WorkerV0 diff --git a/tests/conftest.py b/tests/conftest.py index 7cb2fc0..888a94a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ import pytest from skynet.ipfs import AsyncIPFSHTTP +from skynet.contract import GPUContractAPI from skynet._testing import override_dgpu_config @@ -24,9 +25,11 @@ def skynet_cleos(cleos_bs): # cleos.import_key('gpu.scd', priv) cleos.new_account('gpu.scd', ram=4200000) + contract_path = 'tests/contracts/skygpu-contract/target' cleos.deploy_contract_from_path( 'gpu.scd', - 'tests/contracts/gpu.scd', + contract_path, + contract_name='skygpu', create_account=False ) @@ -37,9 +40,13 @@ def skynet_cleos(cleos_bs): 'gpu.scd' ) - cleos.new_account('testworker') + testworker_key = '5KRPFxF4RJebqPXqRzwStmCaEWeRfp3pR7XUNoA3zCHt5fnPu3s' + pub_key = cleos.import_key('testworker', testworker_key) + cleos.new_account('testworker', key=pub_key) - yield cleos + cleos.wait_blocks(1) + + yield GPUContractAPI(cleos), cleos @pytest.fixture diff --git a/tests/contracts/gpu.scd/gpu.scd.abi b/tests/contracts/gpu.scd/gpu.scd.abi deleted file mode 100644 index f3708bf..0000000 --- a/tests/contracts/gpu.scd/gpu.scd.abi +++ /dev/null @@ -1,416 +0,0 @@ -{ - "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", - "version": "eosio::abi/1.2", - "types": [], - "structs": [ - { - "name": "account", - "base": "", - "fields": [ - { - "name": "user", - "type": "name" - }, - { - "name": "balance", - "type": "asset" - }, - { - "name": "nonce", - "type": "uint64" - } - ] - }, - { - "name": "card", - "base": "", - "fields": [ - { - "name": "id", - "type": "uint64" - }, - { - "name": "owner", - "type": "name" - }, - { - "name": "card_name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "total_memory", - "type": "uint64" - }, - { - "name": "mp_count", - "type": "uint32" - }, - { - "name": "extra", - "type": "string" - } - ] - }, - { - "name": "clean", - "base": "", - "fields": [] - }, - { - "name": "config", - "base": "", - "fields": [ - { - "name": "token_contract", - "type": "name" - }, - { - "name": "token_symbol", - "type": "symbol" - } - ] - }, - { - "name": "dequeue", - "base": "", - "fields": [ - { - "name": "user", - "type": "name" - }, - { - "name": "request_id", - "type": "uint64" - } - ] - }, - { - "name": "enqueue", - "base": "", - "fields": [ - { - "name": "user", - "type": "name" - }, - { - "name": "request_body", - "type": "string" - }, - { - "name": "binary_data", - "type": "string" - }, - { - "name": "reward", - "type": "asset" - }, - { - "name": "min_verification", - "type": "uint32" - } - ] - }, - { - "name": "global_configuration_struct", - "base": "", - "fields": [ - { - "name": "token_contract", - "type": "name" - }, - { - "name": "token_symbol", - "type": "symbol" - } - ] - }, - { - "name": "submit", - "base": "", - "fields": [ - { - "name": "worker", - "type": "name" - }, - { - "name": "request_id", - "type": "uint64" - }, - { - "name": "request_hash", - "type": "checksum256" - }, - { - "name": "result_hash", - "type": "checksum256" - }, - { - "name": "ipfs_hash", - "type": "string" - } - ] - }, - { - "name": "withdraw", - "base": "", - "fields": [ - { - "name": "user", - "type": "name" - }, - { - "name": "quantity", - "type": "asset" - } - ] - }, - { - "name": "work_request_struct", - "base": "", - "fields": [ - { - "name": "id", - "type": "uint64" - }, - { - "name": "user", - "type": "name" - }, - { - "name": "reward", - "type": "asset" - }, - { - "name": "min_verification", - "type": "uint32" - }, - { - "name": "nonce", - "type": "uint64" - }, - { - "name": "body", - "type": "string" - }, - { - "name": "binary_data", - "type": "string" - }, - { - "name": "timestamp", - "type": "time_point_sec" - } - ] - }, - { - "name": "work_result_struct", - "base": "", - "fields": [ - { - "name": "id", - "type": "uint64" - }, - { - "name": "request_id", - "type": "uint64" - }, - { - "name": "user", - "type": "name" - }, - { - "name": "worker", - "type": "name" - }, - { - "name": "result_hash", - "type": "checksum256" - }, - { - "name": "ipfs_hash", - "type": "string" - }, - { - "name": "submited", - "type": "time_point_sec" - } - ] - }, - { - "name": "workbegin", - "base": "", - "fields": [ - { - "name": "worker", - "type": "name" - }, - { - "name": "request_id", - "type": "uint64" - }, - { - "name": "max_workers", - "type": "uint32" - } - ] - }, - { - "name": "workcancel", - "base": "", - "fields": [ - { - "name": "worker", - "type": "name" - }, - { - "name": "request_id", - "type": "uint64" - }, - { - "name": "reason", - "type": "string" - } - ] - }, - { - "name": "worker", - "base": "", - "fields": [ - { - "name": "account", - "type": "name" - }, - { - "name": "joined", - "type": "time_point_sec" - }, - { - "name": "left", - "type": "time_point_sec" - }, - { - "name": "url", - "type": "string" - } - ] - }, - { - "name": "worker_status_struct", - "base": "", - "fields": [ - { - "name": "worker", - "type": "name" - }, - { - "name": "status", - "type": "string" - }, - { - "name": "started", - "type": "time_point_sec" - } - ] - } - ], - "actions": [ - { - "name": "clean", - "type": "clean", - "ricardian_contract": "" - }, - { - "name": "config", - "type": "config", - "ricardian_contract": "" - }, - { - "name": "dequeue", - "type": "dequeue", - "ricardian_contract": "" - }, - { - "name": "enqueue", - "type": "enqueue", - "ricardian_contract": "" - }, - { - "name": "submit", - "type": "submit", - "ricardian_contract": "" - }, - { - "name": "withdraw", - "type": "withdraw", - "ricardian_contract": "" - }, - { - "name": "workbegin", - "type": "workbegin", - "ricardian_contract": "" - }, - { - "name": "workcancel", - "type": "workcancel", - "ricardian_contract": "" - } - ], - "tables": [ - { - "name": "cards", - "type": "card", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "config", - "type": "global_configuration_struct", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "queue", - "type": "work_request_struct", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "results", - "type": "work_result_struct", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "status", - "type": "worker_status_struct", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "users", - "type": "account", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "workers", - "type": "worker", - "index_type": "i64", - "key_names": [], - "key_types": [] - } - ], - "ricardian_clauses": [], - "variants": [], - "action_results": [] -} \ No newline at end of file diff --git a/tests/contracts/gpu.scd/gpu.scd.wasm b/tests/contracts/gpu.scd/gpu.scd.wasm deleted file mode 100755 index 688185263477a3e2acbe25ab4f9eed5839ca1eaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46064 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 A;Q#;t diff --git a/tests/contracts/skygpu-contract b/tests/contracts/skygpu-contract new file mode 160000 index 0000000..87794ff --- /dev/null +++ b/tests/contracts/skygpu-contract @@ -0,0 +1 @@ +Subproject commit 87794ffb45340103159450694c56f241615431b2 diff --git a/tests/test_chain.py b/tests/test_chain.py index 97adfe6..4f23848 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -1,13 +1,231 @@ import trio +import pytest +from leap.errors import TransactionPushError from msgspec import json +from skynet.contract import RequestNotFound, ConfigNotFound from skynet.types import BodyV0, BodyV0Params - from skynet._testing import open_test_worker +async def test_system(skynet_cleos): + gpu, cleos = skynet_cleos + + # assert config can only be init once (done by fixture) + with pytest.raises(TransactionPushError): + await gpu.init_config('eosio.token', '4,TLOS') + + # test clean function + + # fill tables with data + + # accounts + users = [] + quantity = '1.0000 TLOS' + usr_num = 3 + for _ in range(usr_num): + test_user = cleos.new_account() + cleos.transfer_token('eosio', test_user, quantity, 'clean testing') + await gpu.deposit(test_user, quantity) + # will throw if user not found + await gpu.get_user(test_user) + users.append(test_user) + + # queue + for user in users: + await gpu.enqueue( + user, + BodyV0( + method='txt2img', + params=BodyV0Params( + prompt='ayy lmao', + model='skygpu/mocker', + step=1, + seed=0, + guidance=10.0 + ) + ), + min_verification=2 # make reqs stay after 1 result + ) + + # check requests are in queue + queue = await gpu.get_queue() + assert len(queue) == usr_num + + # workers + workers = [] + quantity = '1.0000 TLOS' + wrk_num = 3 + for _ in range(wrk_num): + worker = cleos.new_account() + await gpu.register_worker(worker, 'http://localhost') + # will throw if worker not found + await gpu.get_worker(worker) + workers.append(worker) + + # status + for i in range(wrk_num): + req = queue[i] + worker = workers[i] + await gpu.accept_work(worker, req.id) + # will throw is status not found + await gpu.get_worker_status_for_request(req.id, worker) + + # results + # make one of the workers finish to populate result + await gpu.submit_work( + workers[0], + queue[0].id, + 'ff' * 32, + 'null hash' + ) + + results = await gpu.get_results(queue[0].id) + assert len(results) == 1 + + # all tables populated + + # run clean nuke == false + await gpu.clean_tables() + + # assert tables empty + assert len(await gpu.get_queue()) == 0 + for req in queue: + assert len(await gpu.get_statuses_for_request(req.id)) == 0 + assert len(await gpu.get_results(req.id)) == 0 + + # check config, accounts and workers still there + await gpu.get_config() # raises if not found + assert len(await gpu.get_users()) == usr_num + assert len(await gpu.get_workers()) == wrk_num + + # test nuke + await gpu.clean_tables(nuke=True) + with pytest.raises(ConfigNotFound): + await gpu.get_config() + + assert len(await gpu.get_users()) == 0 + assert len(await gpu.get_workers()) == 0 + + # re init config in case other tests run + await gpu.init_config('eosio.token', '4,TLOS') + + +async def test_balance(skynet_cleos): + gpu, cleos = skynet_cleos + + # create fresh account + account = cleos.new_account() + + # try call withdraw with no user account reg'd + with pytest.raises(TransactionPushError): + await gpu.withdraw(account, '1.0000 TLOS') + + # give tokens and deposit to gpu + quantity = '1000.0000 TLOS' + cleos.transfer_token('eosio', account, quantity) + await gpu.deposit(account, quantity) + + # check if balance increased + account_row = await gpu.get_user(account) + assert account_row.balance == quantity + + # try call withdraw with more than deposited + with pytest.raises(TransactionPushError): + await gpu.withdraw(account, '1000.0001 TLOS') + + # withdraw full correct amount + await gpu.withdraw(account, quantity) + + # check if balance decreased + account_row = await gpu.get_user(account) + assert account_row.balance == '0.0000 TLOS' + +async def test_worker_reg(skynet_cleos): + gpu, cleos = skynet_cleos + + # create fresh account + worker = cleos.new_account() + url = 'https://nvidia.com' + + await gpu.register_worker(worker, url) + + # find and check vals + worker_row = await gpu.get_worker(worker) + assert worker_row.account == worker + assert worker_row.url == url + assert worker_row.joined != '1970-01-01T00:00:00' + assert worker_row.left == '1970-01-01T00:00:00' + + # attempt to register twice + with pytest.raises(TransactionPushError): + await gpu.register_worker(worker, url) + + # unregister + reason = 'testing' + await gpu.unregister_worker(worker, reason) + + worker_row = await gpu.get_worker(worker) + assert worker_row.account == worker + assert worker_row.url == url + assert worker_row.left != '1970-01-01T00:00:00' + + # attempt to unreg twice + with pytest.raises(TransactionPushError): + await gpu.unregister_worker(worker, reason) + + +async def test_queue(skynet_cleos): + gpu, cleos = skynet_cleos + + body = BodyV0( + method='txt2img', + params=BodyV0Params( + prompt='cyberpunk hacker travis bickle dystopic alley graffiti', + model='skygpu/mocker', + step=4, + seed=0, + guidance=10.0 + ) + ) + + # create account + account = cleos.new_account() + quantity = '1000.0000 TLOS' + cleos.transfer_token('eosio', account, quantity) + + # attempt to create request without prev deposit + with pytest.raises(TransactionPushError): + await gpu.enqueue(account, body) + + # deposit tokens into gpu + await gpu.deposit(account, quantity) + + # finally enqueue + req = await gpu.enqueue(account, body) + + # search by id + req_found = await gpu.get_request(req.id) + assert req == req_found + + # search by timestamp + reqs = await gpu.get_requests_since(60 * 60) + assert len(reqs) == 1 + assert reqs[0] == req + + # attempt to dequeue wrong req + with pytest.raises(TransactionPushError): + await gpu.dequeue(account, 999999) + + # dequeue correctly + await gpu.dequeue(account, req.id) + + # check deletion + with pytest.raises(RequestNotFound): + await gpu.get_request(req.id) + async def test_full_flow(inject_mockers, skynet_cleos, ipfs_node): - cleos = skynet_cleos + gpu, cleos = skynet_cleos # create account and deposit tokens into gpu account = cleos.new_account() diff --git a/uv.lock b/uv.lock index c806e12..430c272 100644 --- a/uv.lock +++ b/uv.lock @@ -1599,7 +1599,7 @@ wheels = [ [[package]] name = "py-leap" version = "0.1a35" -source = { editable = "../py-leap" } +source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#20f2e1f74e98e3d75984e8e1eee13c3100c17652" } dependencies = [ { name = "base58" }, { name = "cryptos" }, @@ -1609,33 +1609,6 @@ dependencies = [ { name = "ripemd-hash" }, ] -[package.metadata] -requires-dist = [ - { name = "base58", specifier = ">=2.1.1,<3" }, - { name = "cryptos", specifier = ">=2.0.9,<3" }, - { name = "httpx", specifier = ">=0.28.1,<0.29" }, - { name = "msgspec", specifier = ">=0.19.0" }, - { name = "requests", specifier = "<2.32.0" }, - { name = "ripemd-hash", specifier = ">=1.0.1,<2" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "docker", specifier = ">=6.1.3,<7" }, - { name = "pdbpp", specifier = ">=0.10.3,<0.11" }, - { name = "pytest", specifier = ">=8.3.4,<9" }, - { name = "pytest-trio", specifier = ">=0.8.0,<0.9" }, -] -docs = [ - { name = "sphinx", specifier = "==7.1.2" }, - { name = "sphinx-rtd-theme", specifier = "==1.3.0" }, -] -snaps = [ - { name = "bs4", specifier = ">=0.0.2,<0.0.3" }, - { name = "tdqm", specifier = ">=0.0.1,<0.0.2" }, - { name = "zstandard", specifier = ">=0.21.0,<0.22" }, -] - [[package]] name = "pycparser" version = "2.22" @@ -2152,7 +2125,7 @@ requires-dist = [ { name = "outcome", specifier = ">=1.3.0.post0" }, { name = "pillow", specifier = ">=10.0.1,<11" }, { name = "protobuf", specifier = ">=5.29.3,<6" }, - { name = "py-leap", editable = "../py-leap" }, + { name = "py-leap", git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap" }, { name = "pytz", specifier = "~=2023.3.post1" }, { name = "toml", specifier = ">=0.10.2,<0.11" }, { name = "trio", specifier = ">=0.22.2,<0.23" },