# 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 будет предложено за дополнительную плату реализовывать модули для остальных блокчейнов, в порядке приоритета, указанного выше.
## Заключение
Данные текст является ориентировочным планом действий, без лишних, а возможно и нужных деталий. Все уточнения могут и должны быть обсуждаемы.