# Space-box user story (WIP) ## Tables and fields state table - подразумевает хранение только последнего состояния БЧ в таблице. Блок нужен для понимания последнего обновления historical table - подразумевает хранение исторических состояний поля #### auth module ```sql= /*state*/ CREATE TABLE account ( address TEXT NOT NULL PRIMARY KEY, height BIGINT NOT NULL ); ``` #### bank module ПОЛЕ COIN думаем еще, пока везде JSONB таблица COIN как вариант вместо JSONB или кастом типа ```sql= CREATE TABLE supply /*history*/ ( height BIGINT NOT NULL, supply JSONB NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против, нужно понять производительность, фильтрация по полю в массиве*/ ); ``` ```sql= CREATE TABLE send_message /*history*/ ( height BIGINT NOT NULL, address_from TEXT NOT NULL, address_to TEXT NOT NULL, coins JSONB NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против, нужно понять производительность, фильтрация по полю в массиве*/ tx_hash, msg_index ); ``` ```sql= CREATE TABLE multisend_message /*history*/ ( height BIGINT NOT NULL, address_from TEXT NOT NULL, addresses_to [] NOT NULL, coins [] NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против, нужно понять производительность, фильтрация по полю в массиве*/ tx_hash, msg_index ); ``` ```sql= /*history*/ CREATE TABLE account_balance ( address TEXT NOT NULL PRIMARY KEY, height BIGINT NOT NULL, coins JSONB NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против */ ); ``` #### consensus module genesis - хз как отобразить эту таблицу... надо думать, нужна ли она. среднее время блока во всех видах на следующую итерацию, если понадобится ```sql= /*?*/ CREATE TABLE average_block_time_from_genesis ( height BIGINT NOT NULL, time INT NOT NULL /* ms */ ); ``` среднее время блока во всех видах на следующую итерацию, если понадобится ```sql= /*?*/ CREATE TABLE average_block_time_per_day ( date DATE NOT NULL, time INT NOT NULL /* ms */ ); ``` ```sql= /*history*/ CREATE TABLE block ( height BIGINT UNIQUE PRIMARY KEY, hash TEXT NOT NULL UNIQUE, num_txs INTEGER DEFAULT 0, total_gas BIGINT DEFAULT 0, proposer_address TEXT REFERENCES validator (consensus_address), timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL ); ``` #### distribution module попытаться записывать только блок изменения параметров, чтобы не нагружать базу дубликатами ```sql= /*history*/ CREATE TABLE distribution_params ( params JSONB NOT NULL, height BIGINT NOT NULL ); ``` ```sql= /*history*/ CREATE TABLE community_pool ( coins JSONB NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против */ height BIGINT NOT NULL ); ``` ```sql= /*state*/ CREATE TABLE validator ( consensus_address TEXT NOT NULL PRIMARY KEY, /* Validator consensus address */ consensus_pubkey TEXT NOT NULL UNIQUE /* Validator consensus public key */ ); ``` ```sql= /*state*/ CREATE TABLE validator_info ( consensus_address TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES validator (consensus_address), operator_address TEXT NOT NULL UNIQUE, self_delegate_address TEXT REFERENCES account (address), min_self_delegation BIGINT NOT NULL, height BIGINT NOT NULL ); ``` ```sql= /*history*/ !!!paused!!! CREATE TABLE delegation_reward ( operator_address TEXT NOT NULL REFERENCES validator_info (operator_address), delegator_address TEXT NOT NULL REFERENCES account (address), withdraw_address TEXT NOT NULL, amount(coins) JSONB NOT NULL, height BIGINT NOT NULL ); ``` историческая таблица с распаршенными месседжами от транзакции `cosmos-sdk/MsgWithdrawDelegationReward` ```sql= /*history*/ посмотреть тело транзакций, разобраться с withdraw_address CREATE TABLE delegation_reward_message ( validator_address TEXT NOT NULL REFERENCES validator_info (operator_address), delegator_address TEXT NOT NULL REFERENCES account (address), withdraw_address TEXT NOT NULL, amount(coins) JSONB NOT NULL height, tx_hash, msg_index ); ``` Добавить validator commission, validator description #### gov module ```sql= /*history*/ CREATE TABLE gov_params ( deposit_params JSONB NOT NULL, voting_params JSONB NOT NULL, tally_params JSONB NOT NULL, height BIGINT NOT NULL ); ``` ```sql= CREATE TABLE proposal /*state*/ ( id INTEGER NOT NULL PRIMARY KEY, title TEXT NOT NULL, description TEXT NOT NULL, content JSONB NOT NULL, proposal_route TEXT NOT NULL, proposal_type TEXT NOT NULL, proposal_deposit_amount(coin), submit_time TIMESTAMP NOT NULL, deposit_end_time TIMESTAMP, voting_start_time TIMESTAMP, voting_end_time TIMESTAMP, proposer_address TEXT NOT NULL REFERENCES account (address), status TEXT ); ``` ```sql= /*history, по сути распаршенный месседж deposit proposal*/ CREATE TABLE proposal_deposit_message ( proposal_id INTEGER REFERENCES proposal (id) NOT NULL, depositor_address TEXT REFERENCES account (address), amount COIN[], height BIGINT, tx_hash, msg_index ); ``` ```sql= /*state*/ CREATE TABLE proposal_tally_result ( proposal_id INTEGER REFERENCES proposal (id) PRIMARY KEY, yes BIGINT NOT NULL, abstain BIGINT NOT NULL, no BIGINT NOT NULL, no_with_veto BIGINT NOT NULL ); ``` ```sql= /*history, распаршенный месседж vote*/ CREATE TABLE proposal_vote(message) ( proposal_id INTEGER NOT NULL REFERENCES proposal (id), voter_address TEXT NOT NULL REFERENCES account (address), option TEXT NOT NULL, height BIGINT NOT NULL, tx_hash, msg_index ); ``` #### slashing(?) пока пропускаем ```sql= CREATE TABLE validator_signing_info ( validator_address TEXT NOT NULL PRIMARY KEY, start_height BIGINT NOT NULL, index_offset BIGINT NOT NULL, jailed_until TIMESTAMP WITHOUT TIME ZONE NOT NULL, tombstoned BOOLEAN NOT NULL, missed_blocks_counter BIGINT NOT NULL, height BIGINT NOT NULL ); ``` ```sql= CREATE TABLE slashing_params ( params JSONB NOT NULL, height BIGINT NOT NULL ); ``` #### mint ```sql= CREATE TABLE mint_params ( params JSONB NOT NULL, height BIGINT NOT NULL ); ``` историческая таблица изменения инфляции ```sql= -- CREATE TABLE inflation -- ( -- height BIGINT NOT NULL, -- inflation DECIMAL NOT NULL, -- block_provision JSONB NOT NULL, // доп поле -- annual_provision JSONB NOT NULL // доп поле -- ); ``` историческая таблица изменения годового дохода ```sql= CREATE TABLE annual_provision ( height BIGINT NOT NULL, annual_provision JSONB NOT NULL // доп поле ); ``` #### staking ```sql= /*state*/ CREATE TABLE validator ( consensus_address TEXT NOT NULL PRIMARY KEY, /* Validator consensus address */ consensus_pubkey TEXT NOT NULL UNIQUE, /* Validator consensus public key */ operator_address TEXT NOT NULL UNIQUE, height ); ``` ```sql= /*history*/ CREATE TABLE validator_status ( validator_address TEXT NOT NULL REFERENCES validator (operator_address) PRIMARY KEY, status INT NOT NULL, jailed BOOLEAN NOT NULL, height BIGINT NOT NULL ); ``` ```sql= /*history*/ CREATE TABLE validator_info ( consensus_address TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES validator (consensus_address), operator_address TEXT NOT NULL UNIQUE, self_delegate_address TEXT REFERENCES account (address), min_self_delegation BIGINT NOT NULL, height BIGINT NOT NULL ); ``` ```sql= CREATE TABLE validator_description ( validator_address TEXT NOT NULL REFERENCES validator (operator_address) PRIMARY KEY, moniker TEXT, identity TEXT, avatar_url TEXT, website TEXT, security_contact TEXT, details TEXT, height BIGINT NOT NULL ); ``` ```sql= CREATE TABLE validator_commission ( validator_address TEXT NOT NULL REFERENCES validator (operator_address) PRIMARY KEY, commission DECIMAL NOT NULL, min_self_delegation BIGINT NOT NULL, height BIGINT NOT NULL ); ``` ```sql= /*history*/ CREATE TABLE staking_params ( params JSONB NOT NULL, height BIGINT NOT NULL ); ``` ```sql= /*history*/ CREATE TABLE validator_voting_power(?) ( validator_address TEXT NOT NULL REFERENCES validator (consensus_address) PRIMARY KEY, voting_power BIGINT NOT NULL, height BIGINT NOT NULL REFERENCES block (height) ); ``` ```sql= CREATE TABLE double_sign_vote(?) ( id SERIAL PRIMARY KEY, type SMALLINT NOT NULL, height BIGINT NOT NULL, round INT NOT NULL, block_id TEXT NOT NULL, validator_address TEXT NOT NULL REFERENCES validator (consensus_address), validator_index INT NOT NULL, signature TEXT NOT NULL, UNIQUE (block_id, validator_address) ); ``` ```sql= CREATE TABLE double_sign_evidence(?) ( height BIGINT NOT NULL, vote_a_id BIGINT NOT NULL REFERENCES double_sign_vote (id), vote_b_id BIGINT NOT NULL REFERENCES double_sign_vote (id) ); ``` ```sql= /*history*/ CREATE TABLE staking_pool ( bonded_tokens BIGINT NOT NULL, not_bonded_tokens BIGINT NOT NULL, height BIGINT NOT NULL ); ``` ```sql= /*state*/ CREATE TABLE redelegation ( delegator_address TEXT NOT NULL REFERENCES account (address), src_validator_address TEXT NOT NULL REFERENCES validator (operator_address), dst_validator_address TEXT NOT NULL REFERENCES validator (operator_address), amount(coin) JSONB NOT NULL, completion_time TIMESTAMP WITHOUT TIME ZONE NOT NULL ); ``` ```sql= /*history*/ CREATE TABLE redelegation_message ( delegator_address TEXT NOT NULL REFERENCES account (address), src_validator_address TEXT NOT NULL REFERENCES validator (consensus_address), dst_validator_address TEXT NOT NULL REFERENCES validator (consensus_address), amount(coin) JSONB NOT NULL, completion_time TIMESTAMP WITHOUT TIME ZONE NOT NULL, height BIGINT NOT NULL, tx_hash TEXT NOT NULL, msg_index ); ``` ```sql= /*state*/ CREATE TABLE unbonding_delegation ( validator_address TEXT NOT NULL REFERENCES validator (consensus_address), delegator_address TEXT NOT NULL REFERENCES account (address), amount COIN NOT NUll, completion_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL ); ``` Новая таблица, парсится из месседжа транзакции ```sql= /*history*/ CREATE TABLE unbonding_delegation_message ( validator_address TEXT NOT NULL REFERENCES validator (consensus_address), delegator_address TEXT NOT NULL REFERENCES account (address), amount COIN NOT NUll, completion_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL, height BIGINT NOT NULL, tx_hash TEXT NOT NULL, msg_index ); ``` gRPC? ```sql= /*state*/ CREATE TABLE delegation ( operator_address TEXT NOT NULL validator_info?, delegator_address TEXT NOT NULL REFERENCES account (account), coins JSONB NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против */ height BIGINT NOT NULL ); ``` Новая таблица, парсится из месседжа транзакции ```sql= /*history*/ CREATE TABLE delegation_message ( operator_address TEXT NOT NULL , delegator_address TEXT NOT NULL REFERENCES account (account), coins JSONB NOT NULL /* [{"amount": amount, "denom": denom}] если удобнее ввести тип COIN то я не против */ height BIGINT NOT NULL tx_hash TEXT NOT NULL, msg_index ); ``` #### history ? - account. исторически имеется в таблице аккаунт, есть номер блока, когда добавлен адрес, не актуально - account_balance модуль банк, такая же история - delegation. введена таблица delegation_message, там история - redelegation. также - unbonding_delegation. также - delegation_reward. также - validator_commission_amount. модуль distribution. по сути, если писать блок, то таблица может выполнять 2 функции. if history module enabled: account_balance (get) delegation (get) redelegation (get) unbonding_delegation (get) delegation_reward (get) validator_commission_amount (get) account_balance_history (insert) ```sql= /*history*/ !!!paused!!! -- CREATE TABLE account_full_balance -- ( -- address TEXT NOT NULL REFERENCES account (address), -- balance JSONB NOT NULL DEFAULT '{}', -- delegated JSONB NOT NULL DEFAULT '{}', -- unbonding JSONB NOT NULL DEFAULT '{}', -- redelegating JSONB NOT NULL DEFAULT '{}', -- commission JSONB NOT NULL DEFAULT '{}', -- reward JSONB NOT NULL DEFAULT '{}', -- height BIGINT NOT NULL -- ); ``` ```sql= /*history*/ CREATE TABLE transaction ( hash TEXT NOT NULL UNIQUE PRIMARY KEY, height BIGINT NOT NULL REFERENCES block (height), success BOOLEAN NOT NULL, /* Body */ messages JSONB NOT NULL DEFAULT '[]'::JSONB, memo TEXT, signatures TEXT[] NOT NULL, /* AuthInfo */ signer_infos JSONB NOT NULL DEFAULT '[]'::JSONB, fee JSONB NOT NULL DEFAULT '{}'::JSONB, signer TEXT NOT NULL DEFAULT '{}'::JSONB REFERENCES accounts(address), /* Tx response */ gas_wanted BIGINT DEFAULT 0, gas_used BIGINT DEFAULT 0, raw_log TEXT, logs JSONB, success ); ``` ```sql= /*history*/ CREATE TABLE message ( transaction_hash TEXT NOT NULL REFERENCES transaction (hash), index BIGINT NOT NULL, type TEXT NOT NULL, value JSONB NOT NULL, involved_accounts_addresses TEXT[] NULL signer TEXT NOT NULL DEFAULT '{}'::JSONB REFERENCES accounts(address) ); ``` --- ## Введение Данный текст является ориентировочным заданием на разработку продукта space-box (рабочее название от bro index) в котором будет поставлена идеальная задача (подразумеваются корректировки), разъяснен требуемый функционал, декомпозиция проекта на стейджи (количество, веса), ислючая финансовые детали и дальнешее развитие и поддержка проекта, после выполнения основной части. Проект является open-source, под лицензией MIT, в связи с этим, весь код, ровно как и история разработки должны быть прозрачными и находиться в репозитории организации [bro-n-bro](). Все необходимые ссылки на разработчиков могут быть добавлены к README названного репозитория в любом статусе, за исключением владельцев данного репозитория. Разработка должна быть "чистой", документированной, упорядоченной видом [issues] -> PR -> Merge Ветка master бует закрыта от прямых коммитов. ## Постановка задачи Требуется разработать индексатор блокчейнов, основой которых является [cosmos-sdk](). Данные необходимо записывать в [ClickHouse DB](), также третьим инстансом необходим GraphQL движок, Hasura, в идеале. Все три инстанса должны быть докерезированы и обернуты в единый docker-compose файл для легкого запуска. Конфигурация индексатора перед запуском должна проводиться в файле `.env`: ```bash CHAIN_PREFIX=prefix # префикс индексируемой сети CLICKHOUSE_DB_FOLDER=$HOME/.space-box/clickhouse # место хранения файлов БД CLICKHOUSE_DB_HOST=localhost # хост БД CLICKHOUSE_DB_PORT=5432 # порт БД CLICKHOUSE_DB_NAME=space-box # назване БД CLICKHOUSE_USER_NAME=space-box # аккаунт пользователя БД CLICKHOUSE_DB_PASSWORD=space-box # пароль пользователя БД CLICKHOUSE_SSL_MODE=disable # хз, че это HASURA_PORT=8090 # порт хасуры HASURA_ADMIN_SECRET=hasura # пароль хасуры SPACE_BOX_WORKERS=1 # количество параллельных процессов RPC_URL=http://localhost:26657 # RPC API индексируемой сети GRPC_URL=http://localhost:9090 # GRPC API индексируемой цепи START_HEIGHT=1 # Блок, с которого проводить индексацию START_FROM_SNAPSHOT=False # флан на случай старта неполной ноды, а со снапшота ``` Запуск docker-compose означает поднятия всех необходимых сервисов и инициализации всех таблиц базы данных. Makefile или баш скрипт могут присутствовать. [Пример](https://github.com/cybercongress/cyberindex/blob/main/scripts/start-docker.sh). Также необходимо автоматизировать процесс создания роли `anonymous` в хасуре, с предоставленными этой роли правами на чтение данных таблицы, ровно как и агрегированных данных. Сервис может индексировать блокчейн как с нуля, при наличии архивной ноды, так и с заданного блока, при наличии снэпшота. Данные необходимо индексировать в двух режимах sync и listen, что значит индексация пропущенных блоков с начала функионирования блокчейна и запись по ws свежих блоков. Предполагаемая скорость индексакции 1000 блоков в секунду при задействовании 8 воркеров. ## Индексируемые данные cosmos-sdk предоставляет возможность разработки блокчейна с подключаемыми модулями. Модули, которые можно подключить для индексации тоже можно указать в `.env` файле. Готовым продуктом будет считаться такой индексатор, который может проиндексировать блокчейн cosmoshub, версия cosmos-sdk которого не ниже `v0.45.6`. Также, данные модули должны индексироваться на других cosmos-based блокчейнах, при условии, что версия их сдк не ниже названной. Список сетей по приоритизации: - cosmos cosmos_sdk_version: v0.45.9 - bostrom cosmos_sdk_version: v0.45.9 - juno cosmos_sdk_version: v0.45.9 - osmosis cosmos_sdk_version: v0.46.1 - evmos cosmos_sdk_version: v0.45.10 - stargaze cosmos_sdk_version: v0.45.9 - gravity cosmos_sdk_version: v0.45.9 - crescent cosmos_sdk_version: v0.45.9 - desmos cosmos_sdk_version: v0.45.8 - e-money cosmos_sdk_version: v0.42.11 - omniflix cosmos_sdk_version: v0.45.9 - stride cosmos_sdk_version: v0.45.9 Список модулей приведен [здесь](https://docs.cosmos.network/v0.46/modules/), хорошим примером схемы БД является [такая схема](https://github.com/forbole/bdjuno/tree/cosmos/v0.44.x/database/schema) c некоторыми уточнениями, такими как добавления таблицы с историческими балансами аккаунтов, добавления поля APR в модуле mint таблице inflation, добавление явного вида адреса подписанта транзакции. Уточнение всех таблиц лучше сделать вместе. А также добавление модулей, которые не представлены в этой схеме, например staking, ibc, wasm. ## Реперные точки (стейджи) Предлагается разделить транши за выполнение работы на три стейджа: 1. Реализация модульной архитекторы индексера, упаковка в docker-compose, реализация всего того, что написано в секции постановка задачи и реализация индексации модулей auth, bank, gov, mint, params, slashing, staking. (40%). 2. Добавление остальных модулей: authz, capability, crisis, distribution, epoching, evidence, feegrant, group, nft, upgrade, ibc. (40%) 3. Тестирование, багхантинг, уточнение таблиц (20%) ## Дальнешее развитие и поддержка После успешной реализации базового индексатора для cosmoshub будет предложено за дополнительную плату реализовывать модули для остальных блокчейнов, в порядке приоритета, указанного выше. ## Заключение Данные текст является ориентировочным планом действий, без лишних, а возможно и нужных деталий. Все уточнения могут и должны быть обсуждаемы.