From 1d7d11a9c1e562bc464c40d084646150f445485f Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Sat, 27 May 2023 21:38:04 -0300 Subject: [PATCH] Add .ini example & new account config Add new smart contract clis config etc Update GPU worker software to match contract updates Do dynamic nodeos genesis --- .../Dockerfile | 4 +- .../config.ini | 0 .../contracts/eosio.msig/eosio.msig.abi | 0 .../contracts/eosio.msig/eosio.msig.wasm | Bin .../contracts/eosio.system/eosio.system.abi | 0 .../contracts/eosio.system/eosio.system.wasm | Bin .../contracts/eosio.token/eosio.token.abi | 0 .../contracts/eosio.token/eosio.token.wasm | Bin .../contracts/eosio.wrap/eosio.wrap.abi | 0 .../contracts/eosio.wrap/eosio.wrap.wasm | Bin .../contracts/telos.decide/decide.abi | 0 .../contracts/telos.decide/decide.wasm | Bin .../genesis/skynet.json | 0 skynet.ini.example | 5 + skynet/cli.py | 90 ++++++++++++- skynet/config.py | 18 +++ skynet/dgpu.py | 106 ++++++++++++++-- skynet/frontend/telegram.py | 21 ++-- skynet/nodeos.py | 92 ++++++++++++-- tests/contracts/telos.gpu/telos.gpu.abi | 118 ++++++++++++++++++ tests/contracts/telos.gpu/telos.gpu.wasm | Bin 21761 -> 44297 bytes tests/test_deploy.py | 46 ++----- 22 files changed, 434 insertions(+), 66 deletions(-) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/Dockerfile (79%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/config.ini (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.msig/eosio.msig.abi (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.msig/eosio.msig.wasm (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.system/eosio.system.abi (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.system/eosio.system.wasm (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.token/eosio.token.abi (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.token/eosio.token.wasm (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.wrap/eosio.wrap.abi (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/eosio.wrap/eosio.wrap.wasm (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/telos.decide/decide.abi (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/contracts/telos.decide/decide.wasm (100%) rename docker/{leap-skynet-4.0.0 => leap-skynet-4.0.1}/genesis/skynet.json (100%) diff --git a/docker/leap-skynet-4.0.0/Dockerfile b/docker/leap-skynet-4.0.1/Dockerfile similarity index 79% rename from docker/leap-skynet-4.0.0/Dockerfile rename to docker/leap-skynet-4.0.1/Dockerfile index d529cba..4cd0dec 100644 --- a/docker/leap-skynet-4.0.0/Dockerfile +++ b/docker/leap-skynet-4.0.1/Dockerfile @@ -5,9 +5,9 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y wget # install eosio tools -RUN wget https://github.com/AntelopeIO/leap/releases/download/v4.0.0/leap_4.0.0-ubuntu22.04_amd64.deb +RUN wget https://github.com/AntelopeIO/leap/releases/download/v4.0.1/leap_4.0.1-ubuntu22.04_amd64.deb -RUN apt-get install -y ./leap_4.0.0-ubuntu22.04_amd64.deb +RUN apt-get install -y ./leap_4.0.1-ubuntu22.04_amd64.deb RUN mkdir -p /root/nodeos WORKDIR /root/nodeos diff --git a/docker/leap-skynet-4.0.0/config.ini b/docker/leap-skynet-4.0.1/config.ini similarity index 100% rename from docker/leap-skynet-4.0.0/config.ini rename to docker/leap-skynet-4.0.1/config.ini diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.msig/eosio.msig.abi b/docker/leap-skynet-4.0.1/contracts/eosio.msig/eosio.msig.abi similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.msig/eosio.msig.abi rename to docker/leap-skynet-4.0.1/contracts/eosio.msig/eosio.msig.abi diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.msig/eosio.msig.wasm b/docker/leap-skynet-4.0.1/contracts/eosio.msig/eosio.msig.wasm similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.msig/eosio.msig.wasm rename to docker/leap-skynet-4.0.1/contracts/eosio.msig/eosio.msig.wasm diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.system/eosio.system.abi b/docker/leap-skynet-4.0.1/contracts/eosio.system/eosio.system.abi similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.system/eosio.system.abi rename to docker/leap-skynet-4.0.1/contracts/eosio.system/eosio.system.abi diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.system/eosio.system.wasm b/docker/leap-skynet-4.0.1/contracts/eosio.system/eosio.system.wasm similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.system/eosio.system.wasm rename to docker/leap-skynet-4.0.1/contracts/eosio.system/eosio.system.wasm diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.token/eosio.token.abi b/docker/leap-skynet-4.0.1/contracts/eosio.token/eosio.token.abi similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.token/eosio.token.abi rename to docker/leap-skynet-4.0.1/contracts/eosio.token/eosio.token.abi diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.token/eosio.token.wasm b/docker/leap-skynet-4.0.1/contracts/eosio.token/eosio.token.wasm similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.token/eosio.token.wasm rename to docker/leap-skynet-4.0.1/contracts/eosio.token/eosio.token.wasm diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.wrap/eosio.wrap.abi b/docker/leap-skynet-4.0.1/contracts/eosio.wrap/eosio.wrap.abi similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.wrap/eosio.wrap.abi rename to docker/leap-skynet-4.0.1/contracts/eosio.wrap/eosio.wrap.abi diff --git a/docker/leap-skynet-4.0.0/contracts/eosio.wrap/eosio.wrap.wasm b/docker/leap-skynet-4.0.1/contracts/eosio.wrap/eosio.wrap.wasm similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/eosio.wrap/eosio.wrap.wasm rename to docker/leap-skynet-4.0.1/contracts/eosio.wrap/eosio.wrap.wasm diff --git a/docker/leap-skynet-4.0.0/contracts/telos.decide/decide.abi b/docker/leap-skynet-4.0.1/contracts/telos.decide/decide.abi similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/telos.decide/decide.abi rename to docker/leap-skynet-4.0.1/contracts/telos.decide/decide.abi diff --git a/docker/leap-skynet-4.0.0/contracts/telos.decide/decide.wasm b/docker/leap-skynet-4.0.1/contracts/telos.decide/decide.wasm similarity index 100% rename from docker/leap-skynet-4.0.0/contracts/telos.decide/decide.wasm rename to docker/leap-skynet-4.0.1/contracts/telos.decide/decide.wasm diff --git a/docker/leap-skynet-4.0.0/genesis/skynet.json b/docker/leap-skynet-4.0.1/genesis/skynet.json similarity index 100% rename from docker/leap-skynet-4.0.0/genesis/skynet.json rename to docker/leap-skynet-4.0.1/genesis/skynet.json diff --git a/skynet.ini.example b/skynet.ini.example index 7580e0f..1faf8b8 100644 --- a/skynet.ini.example +++ b/skynet.ini.example @@ -1,3 +1,8 @@ +[skynet.account] +name = xxxxxxxxxxxx +permission = active +key = EOSXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + [skynet.dgpu] hf_home = hf_home hf_token = hf_XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx diff --git a/skynet/cli.py b/skynet/cli.py index 34c8619..4e956f2 100644 --- a/skynet/cli.py +++ b/skynet/cli.py @@ -3,6 +3,7 @@ import os import json import logging +import random from typing import Optional from functools import partial @@ -88,13 +89,15 @@ def download(): @skynet.command() @click.option( - '--account', '-a', default='telegram1') + '--account', '-A', default=None) @click.option( - '--permission', '-p', default='active') + '--permission', '-p', default=None) @click.option( '--key', '-k', default=None) @click.option( '--node-url', '-n', default='http://skynet.ancap.tech') +@click.option( + '--reward', '-r', default='20.0000 GPU') @click.option('--algo', '-a', default='midj') @click.option( '--prompt', '-p', default='a red old tractor in a sunny wheat field') @@ -103,16 +106,22 @@ def download(): @click.option('--height', '-h', default=512) @click.option('--guidance', '-g', default=10) @click.option('--step', '-s', default=26) -@click.option('--seed', '-S', default=420) +@click.option('--seed', '-S', default=None) @click.option('--upscaler', '-U', default='x4') def enqueue( account: str, permission: str, key: str | None, node_url: str, + reward: str, **kwargs ): + key, account, permission = load_account_info( + key, account, permission) with open_cleos(node_url, key=key) as cleos: + if not kwargs['seed']: + kwargs['seed'] = random.randint(0, 10e9) + req = json.dumps({ 'method': 'diffuse', 'params': kwargs @@ -120,11 +129,11 @@ def enqueue( binary = '' ec, out = cleos.push_action( - 'telos.gpu', 'enqueue', [account, req, binary], f'{account}@{permission}' + 'telos.gpu', 'enqueue', [account, req, binary, reward], f'{account}@{permission}' ) - assert ec == 0 print(collect_stdout(out)) + assert ec == 0 @skynet.command() @@ -175,10 +184,73 @@ def dequeue( node_url: str, request_id: int ): + key, account, permission = load_account_info( + key, account, permission) with open_cleos(node_url, key=key) as cleos: ec, out = cleos.push_action( 'telos.gpu', 'dequeue', [account, request_id], f'{account}@{permission}' ) + + print(collect_stdout(out)) + assert ec == 0 + +@skynet.command() +@click.option( + '--account', '-a', default='telegram1') +@click.option( + '--permission', '-p', default='active') +@click.option( + '--key', '-k', default=None) +@click.option( + '--node-url', '-n', default='http://skynet.ancap.tech') +@click.option( + '--verifications', '-v', default=1) +@click.option( + '--token-contract', '-c', default='eosio.token') +@click.option( + '--token-symbol', '-S', default='4,GPU') +def config( + account: str, + permission: str, + key: str | None, + node_url: str, + verifications: int, + token_contract: str, + token_symbol: str +): + key, account, permission = load_account_info( + key, account, permission) + with open_cleos(node_url, key=key) as cleos: + ec, out = cleos.push_action( + 'telos.gpu', 'config', [verifications, token_contract, token_symbol], f'{account}@{permission}' + ) + + print(collect_stdout(out)) + assert ec == 0 + +@skynet.command() +@click.option( + '--account', '-a', default='telegram1') +@click.option( + '--permission', '-p', default='active') +@click.option( + '--key', '-k', default=None) +@click.option( + '--node-url', '-n', default='http://skynet.ancap.tech') +@click.argument('quantity') +def deposit( + account: str, + permission: str, + key: str | None, + node_url: str, + quantity: str +): + key, account, permission = load_account_info( + key, account, permission) + with open_cleos(node_url, key=key) as cleos: + ec, out = cleos.transfer_token(account, 'telos.gpu', quantity) + + print(collect_stdout(out)) assert ec == 0 @skynet.group() @@ -219,6 +291,10 @@ def dgpu( algos: list[str] ): from .dgpu import open_dgpu_node + + key, account, permission = load_account_info( + key, account, permission) + vtestnet = None try: dclient = docker.from_env() @@ -270,6 +346,10 @@ def telegram( db_user: str, db_pass: str ): + + key, account, permission = load_account_info( + key, account, permission) + _, _, tg_token, cfg = init_env_from_config() asyncio.run( run_skynet_telegram( diff --git a/skynet/config.py b/skynet/config.py index 91d6101..95bdddf 100644 --- a/skynet/config.py +++ b/skynet/config.py @@ -37,3 +37,21 @@ def init_env_from_config( tg_token = config['skynet.telegram']['token'] return hf_home, hf_token, tg_token, config + + +def load_account_info( + key, account, permission + file_path=DEFAULT_CONFIG_PATH +): + _, _, _, config = init_env_from_config() + + if not key: + key = config['skynet.account']['key'] + + if not account: + account = config['skynet.account']['name'] + + if not permission: + permission = config['skynet.account']['permission'] + + return diff --git a/skynet/dgpu.py b/skynet/dgpu.py index 01a5310..37820b4 100644 --- a/skynet/dgpu.py +++ b/skynet/dgpu.py @@ -16,7 +16,7 @@ import asks import torch from leap.cleos import CLEOS, default_nodeos_image -from leap.sugar import get_container +from leap.sugar import get_container, collect_stdout from diffusers import ( StableDiffusionPipeline, @@ -102,6 +102,9 @@ async def open_dgpu_node( logging.info(f'resized it to {image.size}') if algo not in models: + if algo not in ALGOS: + raise DGPUComputeError(f'Unknown algo \"{algo}\"') + logging.info(f'{algo} not in loaded models, swapping...') least_used = list(models.keys())[0] for model in models: @@ -169,6 +172,7 @@ async def open_dgpu_node( raise DGPUComputeError('Unsupported compute method') async def get_work_requests_last_hour(): + logging.info('get_work_requests_last_hour') return await cleos.aget_table( 'telos.gpu', 'telos.gpu', 'queue', index_position=2, @@ -177,10 +181,41 @@ async def open_dgpu_node( ) async def get_status_by_request_id(request_id: int): + logging.info('get_status_by_request_id') return await cleos.aget_table( 'telos.gpu', request_id, 'status') + async def get_global_config(): + logging.info('get_global_config') + return (await cleos.aget_table( + 'telos.gpu', 'telos.gpu', 'config'))[0] + + def get_worker_balance(): + logging.info('get_worker_balance') + rows = cleos.get_table( + 'telos.gpu', 'telos.gpu', 'users', + index_position=1, + key_type='name', + lower_bound=account, + upper_bound=account + ) + if len(rows) == 1: + return rows[0]['balance'] + else: + return None + + async def get_user_nonce(user: str): + logging.info('get_user_nonce') + return (await cleos.aget_table( + 'telos.gpu', 'telos.gpu', 'users', + index_position=1, + key_type='name', + lower_bound=user, + upper_bound=user + ))[0]['nonce'] + def begin_work(request_id: int): + logging.info('begin_work') ec, out = cleos.push_action( 'telos.gpu', 'workbegin', @@ -189,7 +224,35 @@ async def open_dgpu_node( ) assert ec == 0 + def cancel_work(request_id: int, reason: str): + logging.info('cancel_work') + ec, out = cleos.push_action( + 'telos.gpu', + 'workcancel', + [account, request_id, reason], + f'{account}@{permission}' + ) + assert ec == 0 + + def maybe_withdraw_all(): + logging.info('maybe_withdraw_all') + balance = get_worker_balance() + if not balance: + return + + balance_amount = float(balance.split(' ')[0]) + if balance_amount > 0: + ec, out = cleos.push_action( + 'telos.gpu', + 'withdraw', + [account, balance], + f'{account}@{permission}' + ) + logging.info(collect_stdout(out)) + assert ec == 0 + async def find_my_results(): + logging.info('find_my_results') return await cleos.aget_table( 'telos.gpu', 'telos.gpu', 'results', index_position=4, @@ -200,6 +263,7 @@ async def open_dgpu_node( ipfs_node = None def publish_on_ipfs(img_sha: str, raw_img: bytes): + logging.info('publish_on_ipfs') img = Image.open(io.BytesIO(raw_img)) img.save(f'tmp/ipfs-docker-staging/image.png') @@ -209,18 +273,30 @@ async def open_dgpu_node( return ipfs_hash - def submit_work(request_id: int, result_hash: str, ipfs_hash: str): + def submit_work( + request_id: int, + request_hash: str, + result_hash: str, + ipfs_hash: str + ): + logging.info('submit_work') ec, out = cleos.push_action( 'telos.gpu', 'submit', - [account, request_id, result_hash, ipfs_hash], + [account, request_id, request_hash, result_hash, ipfs_hash], f'{account}@{permission}' ) + + print(collect_stdout(out)) assert ec == 0 + config = await get_global_config() + with open_ipfs_node() as ipfs_node: try: while True: + maybe_withdraw_all() + queue = await get_work_requests_last_hour() for req in queue: @@ -232,11 +308,18 @@ async def open_dgpu_node( statuses = await get_status_by_request_id(rid) - if len(statuses) < 3: + if len(statuses) < config['verification_amount']: # parse request body = json.loads(req['body']) binary = bytes.fromhex(req['binary_data']) + hash_str = ( + str(await get_user_nonce(req['user'])) + + + req['body'] + ) + logging.info(f'hashing: {hash_str}') + request_hash = sha256(hash_str.encode('utf-8')).hexdigest() # TODO: validate request @@ -244,14 +327,19 @@ async def open_dgpu_node( logging.info(f'working on {body}') begin_work(rid) - img_sha, raw_img = gpu_compute_one( - body['method'], body['params'], binext=binary) - ipfs_hash = publish_on_ipfs(img_sha, raw_img) + try: + img_sha, raw_img = gpu_compute_one( + body['method'], body['params'], binext=binary) - submit_work(rid, img_sha, ipfs_hash) + ipfs_hash = publish_on_ipfs(img_sha, raw_img) - break + submit_work(rid, request_hash, img_sha, ipfs_hash) + + break + + except BaseException as e: + cancel_work(rid, str(e)) else: logging.info(f'request {rid} already beign worked on, skip...') diff --git a/skynet/frontend/telegram.py b/skynet/frontend/telegram.py index 9f3dc2a..f3bef2f 100644 --- a/skynet/frontend/telegram.py +++ b/skynet/frontend/telegram.py @@ -88,6 +88,10 @@ async def run_skynet_telegram( bot = AsyncTeleBot(tg_token) logging.info(f'tg_token: {tg_token}') + async def get_global_config(): + return (await cleos.aget_table( + 'telos.gpu', 'telos.gpu', 'config'))[0] + async with open_database_connection( db_user, db_pass, db_host ) as db_call: @@ -139,6 +143,7 @@ async def run_skynet_telegram( } }) + request_time = datetime.datetime.now().isoformat() ec, out = cleos.push_action( 'telos.gpu', 'enqueue', [account, req, ''], f'{account}@{permission}' ) @@ -150,17 +155,19 @@ async def run_skynet_telegram( request_id = int(out) logging.info(f'{request_id} enqueued.') + config = await get_global_config() + ipfs_hash = None sha_hash = None for i in range(60): - results = cleos.get_table( - 'telos.gpu', 'telos.gpu', 'results', - index_position=2, - key_type='i64', - lower_bound=request_id, - upper_bound=request_id + submits = await hyperion.aget_actions( + account=account, + filter='telos.gpu:submit', + sort='desc', + after=request_Time ) - if len(results) > 0: + actions = submits['actions'] + if len(actions) > 0: ipfs_hash = results[0]['ipfs_hash'] sha_hash = results[0]['result_hash'] break diff --git a/skynet/nodeos.py b/skynet/nodeos.py index 6ddd51f..af1ce00 100644 --- a/skynet/nodeos.py +++ b/skynet/nodeos.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 +import json import time import logging +from datetime import datetime from contextlib import contextmanager as cm import docker @@ -46,7 +48,7 @@ def open_nodeos(cleanup: bool = True): dclient = docker.from_env() vtestnet = get_container( dclient, - 'guilledk/py-eosio:leap-skynet-4.0.0', + 'guilledk/skynet:leap-4.0.1', name='skynet-nodeos', force_unique=True, detach=True, @@ -61,27 +63,76 @@ def open_nodeos(cleanup: bool = True): cleos.start_keosd() + priv, pub = cleos.create_key_pair() + logging.info(f'SUDO KEYS: {(priv, pub)}') + + cleos.setup_wallet(priv) + + genesis = json.dumps({ + "initial_timestamp": datetime.now().isoformat(), + "initial_key": pub, + "initial_configuration": { + "max_block_net_usage": 1048576, + "target_block_net_usage_pct": 1000, + "max_transaction_net_usage": 1048575, + "base_per_transaction_net_usage": 12, + "net_usage_leeway": 500, + "context_free_discount_net_usage_num": 20, + "context_free_discount_net_usage_den": 100, + "max_block_cpu_usage": 200000, + "target_block_cpu_usage_pct": 1000, + "max_transaction_cpu_usage": 150000, + "min_transaction_cpu_usage": 100, + "max_transaction_lifetime": 3600, + "deferred_trx_expiration_window": 600, + "max_transaction_delay": 3888000, + "max_inline_action_size": 4096, + "max_inline_action_depth": 4, + "max_authority_depth": 6 + } + }, indent=4) + + ec, out = cleos.run( + ['bash', '-c', f'echo \'{genesis}\' > /root/skynet.json']) + assert ec == 0 + + place_holder = 'EOS5fLreY5Zq5owBhmNJTgQaLqQ4ufzXSTpStQakEyfxNFuUEgNs1=KEY:5JnvSc6pewpHHuUHwvbJopsew6AKwiGnexwDRc2Pj2tbdw6iML9' + sig_provider = f'{pub}=KEY:{priv}' + nodeos_config_ini = '/root/nodeos/config.ini' + ec, out = cleos.run( + ['bash', '-c', f'sed -i -e \'s/{place_holder}/{sig_provider}/g\' {nodeos_config_ini}']) + assert ec == 0 + cleos.start_nodeos_from_config( - '/root/nodeos/config.ini', + nodeos_config_ini, data_dir='/root/nodeos/data', - genesis='/root/nodeos/genesis/skynet.json', + genesis='/root/skynet.json', state_plugin=True) time.sleep(0.5) - - public_dev_key = 'EOS5fLreY5Zq5owBhmNJTgQaLqQ4ufzXSTpStQakEyfxNFuUEgNs1' - cleos.setup_wallet('5JnvSc6pewpHHuUHwvbJopsew6AKwiGnexwDRc2Pj2tbdw6iML9') cleos.wait_blocks(1) cleos.boot_sequence(token_sym=Symbol('GPU', 4)) - cleos.new_account('telos.gpu', ram=300000, key=public_dev_key) + priv, pub = cleos.create_key_pair() + cleos.import_key(priv) + logging.info(f'GPU KEYS: {(priv, pub)}') + cleos.new_account('telos.gpu', ram=4200000, key=pub) for i in range(1, 4): + priv, pub = cleos.create_key_pair() + cleos.import_key(priv) + logging.info(f'testworker{i} KEYS: {(priv, pub)}') cleos.create_account_staked( - 'eosio', f'testworker{i}', key=public_dev_key) + 'eosio', f'testworker{i}', key=pub) + priv, pub = cleos.create_key_pair() + cleos.import_key(priv) + logging.info(f'TELEGRAM KEYS: {(priv, pub)}') cleos.create_account_staked( - 'eosio', 'telegram1', ram=500000, key=public_dev_key) + 'eosio', 'telegram', ram=500000, key=pub) + + cleos.transfer_token( + 'eosio', 'telegram', '1000000.0000 GPU', 'Initial testing funds') cleos.deploy_contract_from_host( 'telos.gpu', @@ -90,6 +141,29 @@ def open_nodeos(cleanup: bool = True): create_account=False ) + ec, out = cleos.push_action( + 'telos.gpu', + 'config', + [1, 'eosio.token', '4,GPU'], + f'telos.gpu@active' + ) + assert ec == 0 + + ec, out = cleos.transfer_token( + 'telegram', 'telos.gpu', '1000000.0000 GPU', 'Initial testing funds') + assert ec == 0 + + user_row = cleos.get_table( + 'telos.gpu', + 'telos.gpu', + 'users', + index_position=1, + key_type='name', + lower_bound='telegram', + upper_bound='telegram' + ) + assert len(user_row) == 1 + yield cleos finally: diff --git a/tests/contracts/telos.gpu/telos.gpu.abi b/tests/contracts/telos.gpu/telos.gpu.abi index 75240ee..406c268 100644 --- a/tests/contracts/telos.gpu/telos.gpu.abi +++ b/tests/contracts/telos.gpu/telos.gpu.abi @@ -3,6 +3,47 @@ "version": "eosio::abi/1.2", "types": [], "structs": [ + { + "name": "account", + "base": "", + "fields": [ + { + "name": "user", + "type": "name" + }, + { + "name": "balance", + "type": "asset" + }, + { + "name": "nonce", + "type": "uint64" + } + ] + }, + { + "name": "clean", + "base": "", + "fields": [] + }, + { + "name": "config", + "base": "", + "fields": [ + { + "name": "verification_amount", + "type": "uint8" + }, + { + "name": "token_contract", + "type": "name" + }, + { + "name": "token_symbol", + "type": "symbol" + } + ] + }, { "name": "dequeue", "base": "", @@ -32,6 +73,28 @@ { "name": "binary_data", "type": "bytes" + }, + { + "name": "reward", + "type": "asset" + } + ] + }, + { + "name": "global_configuration_struct", + "base": "", + "fields": [ + { + "name": "verification_amount", + "type": "uint8" + }, + { + "name": "token_contract", + "type": "name" + }, + { + "name": "token_symbol", + "type": "symbol" } ] }, @@ -47,6 +110,10 @@ "name": "request_id", "type": "uint64" }, + { + "name": "request_hash", + "type": "checksum256" + }, { "name": "result_hash", "type": "checksum256" @@ -57,6 +124,20 @@ } ] }, + { + "name": "withdraw", + "base": "", + "fields": [ + { + "name": "user", + "type": "name" + }, + { + "name": "quantity", + "type": "asset" + } + ] + }, { "name": "work_request_struct", "base": "", @@ -69,6 +150,10 @@ "name": "user", "type": "name" }, + { + "name": "reward", + "type": "asset" + }, { "name": "body", "type": "string" @@ -142,6 +227,10 @@ { "name": "request_id", "type": "uint64" + }, + { + "name": "reason", + "type": "string" } ] }, @@ -165,6 +254,16 @@ } ], "actions": [ + { + "name": "clean", + "type": "clean", + "ricardian_contract": "" + }, + { + "name": "config", + "type": "config", + "ricardian_contract": "" + }, { "name": "dequeue", "type": "dequeue", @@ -180,6 +279,11 @@ "type": "submit", "ricardian_contract": "" }, + { + "name": "withdraw", + "type": "withdraw", + "ricardian_contract": "" + }, { "name": "workbegin", "type": "workbegin", @@ -192,6 +296,13 @@ } ], "tables": [ + { + "name": "config", + "type": "global_configuration_struct", + "index_type": "i64", + "key_names": [], + "key_types": [] + }, { "name": "queue", "type": "work_request_struct", @@ -212,6 +323,13 @@ "index_type": "i64", "key_names": [], "key_types": [] + }, + { + "name": "users", + "type": "account", + "index_type": "i64", + "key_names": [], + "key_types": [] } ], "ricardian_clauses": [], diff --git a/tests/contracts/telos.gpu/telos.gpu.wasm b/tests/contracts/telos.gpu/telos.gpu.wasm index de8c6072459cd11de3bed0bb970b8e529be3394a..18dc265b2fdcba8074df182b4c7190f7a0936659 100755 GIT binary patch literal 44297 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ΝWA?KgS$BT`o1XgvG*T9wq6;|c$n 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 HSeX9@n1wEE literal 21761 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�r+2yon?)zDB)%q!%o*1OO_#dUi{17? z=W;$FYFy53_t?^^VfQ`m8e_fg$MzqYp?|vAUOIM|pFqs{S)a!B2d3w=(gEfKg!%oQ z_JQtFIy0Acr}rP`!CmPz?+>*X(*yH!-9>&0kddEfzyHZ-3CtQaasD z`FY0qnK>pt!Y?zXhi6!wp({_g#+B{G#rZ`5nLao(cXtX@i_`oPAaB`;XuMoMp}5rL z?GdKwwvQ|bY6zwN5DD4j8PsBID16lHpgAtieFw7KIKAFYJ1K> z_aiW8W{Mx8Y@c8)zN%Iy=QtIwE5n&QSS>lTWXD%Uv~rr%Ak$w ojv;#s2r6{+q6FRUnmNG_TJFEM+g_5WB0$Fuc%fqK*fq}m7wF7U%m4rY diff --git a/tests/test_deploy.py b/tests/test_deploy.py index 17e1a15..3598788 100644 --- a/tests/test_deploy.py +++ b/tests/test_deploy.py @@ -15,8 +15,7 @@ from leap.sugar import collect_stdout def test_enqueue_work(cleos): - - user = cleos.new_account() + user = 'telegram' req = json.dumps({ 'method': 'diffuse', 'params': { @@ -33,7 +32,7 @@ def test_enqueue_work(cleos): binary = '' ec, out = cleos.push_action( - 'telos.gpu', 'enqueue', [user, req, binary], f'{user}@active' + 'telos.gpu', 'enqueue', [user, req, binary, '20.0000 GPU'], f'{user}@active' ) assert ec == 0 @@ -48,44 +47,23 @@ def test_enqueue_work(cleos): assert req_on_chain['body'] == req assert req_on_chain['binary_data'] == binary - ipfs_hash = None - sha_hash = None - for i in range(1, 4): - trio.run( - partial( - open_dgpu_node, - f'testworker{i}', - 'active', - cleos, - initial_algos=['midj'] - ) + trio.run( + partial( + open_dgpu_node, + f'testworker1', + 'active', + cleos, + initial_algos=['midj'] ) - - if ipfs_hash == None: - result = cleos.get_table( - 'telos.gpu', 'telos.gpu', 'results', - index_position=4, - key_type='name', - lower_bound=f'testworker{i}', - upper_bound=f'testworker{i}' - ) - assert len(result) == 1 - ipfs_hash = result[0]['ipfs_hash'] - sha_hash = result[0]['result_hash'] + ) queue = cleos.get_table('telos.gpu', 'telos.gpu', 'queue') assert len(queue) == 0 - resp = requests.get(f'https://ipfs.io/ipfs/{ipfs_hash}/image.png') - assert resp.status_code == 200 - - assert sha_hash == sha256(resp.content).hexdigest() - def test_enqueue_dequeue(cleos): - - user = cleos.new_account() + user = 'telegram' req = json.dumps({ 'method': 'diffuse', 'params': { @@ -102,7 +80,7 @@ def test_enqueue_dequeue(cleos): binary = '' ec, out = cleos.push_action( - 'telos.gpu', 'enqueue', [user, req, binary], f'{user}@active' + 'telos.gpu', 'enqueue', [user, req, binary, '20.0000 GPU'], f'{user}@active' ) assert ec == 0