mirror of https://github.com/skygpu/skynet.git
First version integrated to rust contract
parent
6a991561de
commit
5bcda18a20
|
@ -7,14 +7,57 @@ jobs:
|
||||||
name: Pytest Tests
|
name: Pytest Tests
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
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
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@v5
|
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
|
- uses: actions/cache@v3
|
||||||
name: Cache venv
|
name: Cache venv
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "tests/contracts/skygpu-contract"]
|
||||||
|
path = tests/contracts/skygpu-contract
|
||||||
|
url = https://github.com/skygpu/skygpu-contract.git
|
|
@ -78,6 +78,7 @@ torch = { index = "torch" }
|
||||||
triton = { index = "torch" }
|
triton = { index = "torch" }
|
||||||
torchvision = { index = "torch" }
|
torchvision = { index = "torch" }
|
||||||
py-leap = { git = "https://github.com/guilledk/py-leap.git", branch = "struct_unwrap" }
|
py-leap = { git = "https://github.com/guilledk/py-leap.git", branch = "struct_unwrap" }
|
||||||
|
# py-leap = { path = "../py-leap", editable = true}
|
||||||
pytest-dockerctl = { git = "https://github.com/pikers/pytest-dockerctl.git", branch = "g_update" }
|
pytest-dockerctl = { git = "https://github.com/pikers/pytest-dockerctl.git", branch = "g_update" }
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
|
|
@ -16,13 +16,14 @@ async def open_test_worker(
|
||||||
cleos, ipfs_node,
|
cleos, ipfs_node,
|
||||||
account: str = 'testworker',
|
account: str = 'testworker',
|
||||||
permission: str = 'active',
|
permission: str = 'active',
|
||||||
|
key: str = '5KRPFxF4RJebqPXqRzwStmCaEWeRfp3pR7XUNoA3zCHt5fnPu3s',
|
||||||
hf_token: str = '',
|
hf_token: str = '',
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
config = override_dgpu_config(
|
config = override_dgpu_config(
|
||||||
account=account,
|
account=account,
|
||||||
permission=permission,
|
permission=permission,
|
||||||
key=cleos.private_keys[account],
|
key=key,
|
||||||
node_url=cleos.endpoint,
|
node_url=cleos.endpoint,
|
||||||
ipfs_url=ipfs_node[1].endpoint,
|
ipfs_url=ipfs_node[1].endpoint,
|
||||||
hf_token=hf_token,
|
hf_token=hf_token,
|
||||||
|
|
|
@ -273,221 +273,3 @@ TG_MAX_WIDTH = 1280
|
||||||
TG_MAX_HEIGHT = 1280
|
TG_MAX_HEIGHT = 1280
|
||||||
|
|
||||||
DEFAULT_SINGLE_CARD_MAP = 'cuda:0'
|
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": []
|
|
||||||
}
|
|
||||||
|
|
|
@ -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]
|
||||||
|
)
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from hashlib import sha256
|
|
||||||
|
|
||||||
import trio
|
import trio
|
||||||
import msgspec
|
import msgspec
|
||||||
|
@ -22,7 +21,7 @@ from skynet.dgpu.network import (
|
||||||
async def maybe_update_tui_balance(conn: NetConnector):
|
async def maybe_update_tui_balance(conn: NetConnector):
|
||||||
async def _fn(tui):
|
async def _fn(tui):
|
||||||
# update balance
|
# 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}')
|
tui.set_header_text(new_balance=f'balance: {balance}')
|
||||||
|
|
||||||
await maybe_update_tui_async(_fn)
|
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'
|
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
|
total_step = body.params.step
|
||||||
mode = body.method
|
mode = body.method
|
||||||
|
|
||||||
# TODO: validate request
|
# 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:
|
if not resp or 'code' in resp:
|
||||||
logging.info('begin_work error, probably being worked on already... skip.')
|
logging.info('begin_work error, probably being worked on already... skip.')
|
||||||
return
|
return
|
||||||
|
@ -157,7 +144,9 @@ async def maybe_serve_one(
|
||||||
|
|
||||||
ipfs_hash = await conn.publish_on_ipfs(output, typ=output_type)
|
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)
|
await maybe_update_tui_balance(conn)
|
||||||
|
|
||||||
|
@ -166,8 +155,10 @@ async def maybe_serve_one(
|
||||||
if 'network cancel' not in str(err):
|
if 'network cancel' not in str(err):
|
||||||
logging.exception('Failed to serve model request !?\n')
|
logging.exception('Failed to serve model request !?\n')
|
||||||
|
|
||||||
|
await state_mngr.update_state()
|
||||||
|
|
||||||
if state_mngr.is_request_in_progress(req.id):
|
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(
|
async def dgpu_serve_forever(
|
||||||
|
|
|
@ -15,18 +15,15 @@ import outcome
|
||||||
import msgspec
|
import msgspec
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from leap.cleos import CLEOS
|
from leap.cleos import CLEOS
|
||||||
from leap.protocol import Asset
|
|
||||||
from skynet.dgpu.tui import maybe_update_tui
|
from skynet.dgpu.tui import maybe_update_tui
|
||||||
from skynet.config import DgpuConfig as Config, load_skynet_toml
|
from skynet.config import DgpuConfig as Config, load_skynet_toml
|
||||||
from skynet.types import (
|
from skynet.types import (
|
||||||
ConfigV0,
|
|
||||||
AccountV0,
|
|
||||||
BodyV0,
|
BodyV0,
|
||||||
RequestV0,
|
RequestV1,
|
||||||
WorkerStatusV0,
|
WorkerStatusV0,
|
||||||
ResultV0
|
ResultV0
|
||||||
)
|
)
|
||||||
from skynet.constants import GPU_CONTRACT_ABI
|
from skynet.contract import GPUContractAPI
|
||||||
|
|
||||||
from skynet.ipfs import (
|
from skynet.ipfs import (
|
||||||
AsyncIPFSHTTP,
|
AsyncIPFSHTTP,
|
||||||
|
@ -70,178 +67,16 @@ class NetConnector:
|
||||||
def __init__(self, config: Config):
|
def __init__(self, config: Config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.cleos = CLEOS(endpoint=config.node_url)
|
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)
|
self.ipfs_client = AsyncIPFSHTTP(config.ipfs_url)
|
||||||
|
|
||||||
maybe_update_tui(lambda tui: tui.set_header_text(new_worker_name=self.config.account))
|
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
|
# IPFS helpers
|
||||||
async def publish_on_ipfs(self, raw, typ: str = 'png'):
|
async def publish_on_ipfs(self, raw, typ: str = 'png'):
|
||||||
Path('ipfs-staging').mkdir(exist_ok=True)
|
Path('ipfs-staging').mkdir(exist_ok=True)
|
||||||
|
@ -302,9 +137,10 @@ class ContractState:
|
||||||
def __init__(self, conn: NetConnector):
|
def __init__(self, conn: NetConnector):
|
||||||
self._conn = conn
|
self._conn = conn
|
||||||
|
|
||||||
|
self._config = load_skynet_toml().dgpu
|
||||||
self._poll_index = 0
|
self._poll_index = 0
|
||||||
|
|
||||||
self._queue: list[RequestV0] = []
|
self._queue: list[RequestV1] = []
|
||||||
self._status_by_rid: dict[int, list[WorkerStatusV0]] = {}
|
self._status_by_rid: dict[int, list[WorkerStatusV0]] = {}
|
||||||
self._results: list[ResultV0] = []
|
self._results: list[ResultV0] = []
|
||||||
|
|
||||||
|
@ -315,10 +151,10 @@ class ContractState:
|
||||||
return self._poll_index
|
return self._poll_index
|
||||||
|
|
||||||
async def _fetch_results(self):
|
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):
|
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):
|
async def update_state(self):
|
||||||
'''
|
'''
|
||||||
|
@ -326,7 +162,7 @@ class ContractState:
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# raw queue from chain
|
# 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
|
# filter out invalids
|
||||||
self._queue = []
|
self._queue = []
|
||||||
|
@ -380,7 +216,7 @@ class ContractState:
|
||||||
return len(self._queue)
|
return len(self._queue)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def first(self) -> RequestV0 | None:
|
def first(self) -> RequestV1 | None:
|
||||||
if len(self._queue) > 0:
|
if len(self._queue) > 0:
|
||||||
return self._queue[0]
|
return self._queue[0]
|
||||||
|
|
||||||
|
@ -391,7 +227,7 @@ class ContractState:
|
||||||
return set((
|
return set((
|
||||||
status.worker
|
status.worker
|
||||||
for status in self._status_by_rid[request_id]
|
for status in self._status_by_rid[request_id]
|
||||||
if status.worker != self._conn.config.account
|
if status.worker != self._config.account
|
||||||
))
|
))
|
||||||
|
|
||||||
# predicates
|
# predicates
|
||||||
|
@ -406,7 +242,7 @@ class ContractState:
|
||||||
|
|
||||||
def should_compete_for_id(self, request_id: int) -> bool:
|
def should_compete_for_id(self, request_id: int) -> bool:
|
||||||
return bool(
|
return bool(
|
||||||
self._conn.config.non_compete &
|
self._config.non_compete &
|
||||||
self.competitors_for_id(request_id)
|
self.competitors_for_id(request_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,25 @@ class ConfigV0:
|
||||||
token_contract: str
|
token_contract: str
|
||||||
token_symbol: 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
|
RequestV0
|
||||||
|
|
||||||
|
@ -102,6 +121,38 @@ class RequestV0(Struct):
|
||||||
binary_data: str
|
binary_data: str
|
||||||
timestamp: 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
|
AccountV0
|
||||||
|
@ -127,6 +178,27 @@ class AccountV0(Struct):
|
||||||
balance: str
|
balance: str
|
||||||
nonce: int
|
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
|
WorkerV0
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from skynet.ipfs import AsyncIPFSHTTP
|
from skynet.ipfs import AsyncIPFSHTTP
|
||||||
|
from skynet.contract import GPUContractAPI
|
||||||
from skynet._testing import override_dgpu_config
|
from skynet._testing import override_dgpu_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,9 +25,11 @@ def skynet_cleos(cleos_bs):
|
||||||
# cleos.import_key('gpu.scd', priv)
|
# cleos.import_key('gpu.scd', priv)
|
||||||
cleos.new_account('gpu.scd', ram=4200000)
|
cleos.new_account('gpu.scd', ram=4200000)
|
||||||
|
|
||||||
|
contract_path = 'tests/contracts/skygpu-contract/target'
|
||||||
cleos.deploy_contract_from_path(
|
cleos.deploy_contract_from_path(
|
||||||
'gpu.scd',
|
'gpu.scd',
|
||||||
'tests/contracts/gpu.scd',
|
contract_path,
|
||||||
|
contract_name='skygpu',
|
||||||
create_account=False
|
create_account=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,9 +40,13 @@ def skynet_cleos(cleos_bs):
|
||||||
'gpu.scd'
|
'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
|
@pytest.fixture
|
||||||
|
|
|
@ -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": []
|
|
||||||
}
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 87794ffb45340103159450694c56f241615431b2
|
|
@ -1,13 +1,231 @@
|
||||||
import trio
|
import trio
|
||||||
|
import pytest
|
||||||
|
from leap.errors import TransactionPushError
|
||||||
from msgspec import json
|
from msgspec import json
|
||||||
|
|
||||||
|
from skynet.contract import RequestNotFound, ConfigNotFound
|
||||||
from skynet.types import BodyV0, BodyV0Params
|
from skynet.types import BodyV0, BodyV0Params
|
||||||
|
|
||||||
from skynet._testing import open_test_worker
|
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):
|
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
|
# create account and deposit tokens into gpu
|
||||||
account = cleos.new_account()
|
account = cleos.new_account()
|
||||||
|
|
24
uv.lock
24
uv.lock
|
@ -1538,17 +1538,17 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psutil"
|
name = "psutil"
|
||||||
version = "6.1.1"
|
version = "7.0.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502 }
|
sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511 },
|
{ url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985 },
|
{ url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488 },
|
{ url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477 },
|
{ url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017 },
|
{ url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602 },
|
{ url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444 },
|
{ url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1598,7 +1598,7 @@ wheels = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "py-leap"
|
name = "py-leap"
|
||||||
version = "0.1a35"
|
version = "0.1a35"
|
||||||
source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#18b3c73e724922a060db5f8ea2b9d9727b6152cc" }
|
source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#04be5045683c198504981ae4b2df8e9ed2361491" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "base58" },
|
{ name = "base58" },
|
||||||
{ name = "cryptos" },
|
{ name = "cryptos" },
|
||||||
|
@ -2195,7 +2195,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tb-nightly"
|
name = "tb-nightly"
|
||||||
version = "2.19.0a20250211"
|
version = "2.19.0a20250213"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "absl-py" },
|
{ name = "absl-py" },
|
||||||
|
@ -2210,7 +2210,7 @@ dependencies = [
|
||||||
{ name = "werkzeug" },
|
{ name = "werkzeug" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/90/f0c8effd911d95e4eba1c10836fd446a9cac1f68e62b858b2177aaae963d/tb_nightly-2.19.0a20250211-py3-none-any.whl", hash = "sha256:d2ba2da592308980a4380da77777c0634e618ef2541155717a903986dc0d7adf", size = 5503304 },
|
{ url = "https://files.pythonhosted.org/packages/9e/ea/c643e6a50e0296bceac6cb28937d35a7042ab1bb7622fa98399862ff3a0b/tb_nightly-2.19.0a20250213-py3-none-any.whl", hash = "sha256:1c6f0e1ce993e05ed36b68e5cbe73280f040eb97d0e6b28d339ef10ed96f177c", size = 5503304 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
Loading…
Reference in New Issue