Skip to content

Описание протокола banker gateway

biobdeveloper edited this page Jun 22, 2020 · 5 revisions

Шлюзы и банкер взаимодействуют между собой через протокол удалённого вызова процедур JSONRPC поверх TCP-соединения ZeroMQ.

Терминология

  • Биржа - децентрализованная криптовалютная биржа как совокупность всех микросервисов (UI, API, Banker, Gateways) являющаяся надстройкой над целевым блокчейном

  • Целевой (Target) блокчейн - блокчейн, в котором выпускаются деривативы и происходит матчинг ордеров

  • Родительский (Native) блокчейн - внешний блокчейн, шлюз которого принимает транзакции на депозит и производит выплаты

Order есть сущность, хранящая пару транзакций (in и out) и некие метаданные ордера, доступ к которым шлюзы не должны иметь.

Order создается ТОЛЬКО банкером. Он создается в случаях:

  • запрос из пользовательского интерфейса "get_deposit_address"
  • обнаружение транзакции в одном из блокчейнов

Order представлен в таблице банкера следующими полями:

далее все для наглядности будет описано как код python

from uuid import UUID
from time import time
from decimal import Decimal

pk: int

# Генерируется исключительно на стороне банкера, дабы избежать каши и коллизий.
order_id: UUID 
 
# см. класс OrderType
# Разделение на Deposit и Withdraw необходимо (для статистики и понимания логики, ибо без них у нас не БИРЖА с торговлей деривативами, а простой обменник) и существует
# только внутри Банкера. Ни один шлюз не понимает, что он принимает - выводы или пополнения.
# определить тип можно по полю in_tx_coin:
# GATEWAY_PREFIX = "FINTEH"
# if GATEWAY_PREFIX in in_tx_coin:
#       order_type = "Withdrawal"
# else:
#        order_type = "Deposit"
order_type: int 

# Предполагаю таблицу User, а это будет внешний ключ
user: int

# Становится True когда in_tx_status и out_tx_status имеют значение 3 (RECEIVED_AND_CONFIRMED)
comleted: bool = False 


"""
in_-транзакция. Транзакция инициализированная юзером. 
Первой всегда инициируется она
"""

# Валюта транзакции. Для депозитов 'COIN', для выводов 'FINTEH.COIN'
in_tx_coin: str 

# Получатель транзакции. Для депозитов это горячий кошелек шлюза, для выводов это имя битшаровского аккаунта шлюза
in_tx_to: str 

# отправитель транзакции. 
# Для депозитов адрес кошелька юзера в блокчейне(в некоторых блокчейнах и реализациях шлюзов может быть пустым), 
# для выводов - имя юзера в битшаре (или другом целевом блокчейне)
in_tx_from: str 

# Хэшстринг из блокчейна в котором произошла транзакция
in_tx_hash: str 

# Вещественное число (с примененным preсision)
in_tx_amount: Decimal

# см. class TxStatus
# Шлюз сам решает, когда наступило то или иное состояние, 
# в зависимости от настроек шлюза(необходимого кол-ва подтверждений
in_tx_status: int 

# Количество подтверждений
# Для блокчейнов с irreversible state предлагаю использовать 0 и 1 как ненаступивше и наступившее irreversible соответственно
in_tx_confirmations: int 
in_tx_created_at: time() # время транслирования транзакции пользователем
in_tx_error: int = 0 # см. class TxError


"""
out_-транзакция. Транзакция инициализированная банкером, в ответ на in_транзакцию.
инизиализируется только при in_tx_status == TxStates.RECEIVED_AND_CONFIRMED
"""

# Валюта транзакции. Для депозитов 'FINTEH.COIN', для выводов 'COIN'
out_tx_coin: str 

# Получатель транзакции.
# Для депозитов это имя юзера в битшаре (или в другом целевом чейне)
# для выводов 'это горячий кошелек шлюза,
out_tx_to: str 

# отправитель транзакции. 
# Для депозитов адрес кошелька в блокчейне(в некоторых блокчейнах и реализациях шлюзов может быть пустым), 
# для выводов - имя юзера в битшаре (или другом целевом блокчейне)
out_tx_from: str 

# Хэшстринг из блокчейна в котором произошла транзакция
out_tx_hash: str 

# Вещественное число (с примененным preсision)
out_tx_amount: Decimal

# см. class TxStatus
# Шлюз сам решает, когда наступило то или иное состояние, 
# в зависимости от настроек шлюза(необходимого кол-ва подтверждений
out_tx_status: int 

# Количество подтверждений
# Для блокчейнов с irreversible state предлагаю использовать 0 и 1 как ненаступивше и наступившее irreversible соответственно
out_tx_confirmations: int 
out_tx_created_at: time() # время транслирования транзакции пользователем
out_tx_error: int = 0 # см. class TxError

Опишем возможные значения order_type

from enum import Enum

class OrderType(Enum):
    TRASH = 0
    DEPOSIT = 1
    WITHDRAWAL = 2

Опишем возможные значения out_tx_status и in_tx_status

from enum import Enum

class TxStates(Enum):
    ERROR = 0
    WAIT = 1
    RECEIVED_NOT_CONFIRMED = 2
    RECEIVED_AND_CONFIRMED = 3

Опишем возможные значения in_tx_error и out_tx_error

from enum import Enum

class TxError(Enum):
    NO_ERROR = 0
    UNKNOWN_ERROR = 1
    ... # TODO

Каждый шлюз ведет у себя внутренню таблицу, подобную этой, со своими нюансами(например, в битшаре также понадобится operation_id, в монеро sub-адреса и т.п.).

Запросы от Banker к Gateway

  • get_deposit_address
    Банкер просит о депозитном адресе для пользователя. В случае с битшарой тупо отдается константа, с эфиром и т.п. генерятся адреса
from uuid import UUID
request = {
"jsonrpc": "2.0",
"method": "get_deposit_address",
"id": 666,
"params": 
    {
    "order_id": UUID, #Если шлюз принял этот запрос изначально от пользователя, значит у него уже есть order_id
    "user": "bitshares_name" # Имя пользователя на бирже. Пока наш целевой блокчейн - Битшара, то это битшаровское имя аккаунта
    }
}

##################

response = {
"jsonrpc": "2.0",
"result": "finteh-account",
"id": 666
}

new_order и update_order Банкер уведомляет шлюз о новом ордере или об обновлении старого

request = {
'jsonrpc': "2.0",
"method": "new_order" or "update_order",
"id": 666,
"params":
    # Если update_order, то кроме (order_id) присылаются только те поля, которые обновились
    [
        {
        "order_id": ...,

        "in_tx_to": ...,
        "in_tx_from": ...,
        "in_tx_coin": ...,
        "in_tx_hash": "",
        "in_tx_amount": ...,
        "in_tx_status": ...,
        "in_tx_created_at": ...,

        "out_tx_to": ...,

        }
    ]
}

##################

response = {"jsonrpc": "2.0",
"result": "ok",
"id": 666}

Запросы от Gateway к Banker

  • new_tx
    Шлюз уведомляет банкер о новой транзакции и получает order_id.
request = {
"jsonrpc": "2.0",
"method": "new_tx",
"id": 666,
"params":
    {
    # Order id не будет, потому что в битшаре любой аккаунт 
    #может зайти через чужую морду, купить наши токены и попробовать вывести
    # order_id создат и вернет банкер

    "in_tx_to": ...,
    "in_tx_from": ...,
    "in_tx_coin": ...,
    "in_tx_hash": "",
    "in_tx_amount": ...,
    "in_tx_status": ...,
    "in_tx_created_at": ...,

    "out_tx_to": ...,
    }
}

##############

response = {"jsonrpc": "2.0",
"result": int, # order_id, который банкер присвоит ордеру
"id": 666}
  • update_order
    Шлюз видит изменения в ордере и уведомляет об этом банкер
from uuid import UUID

request = {
"jsonrpc": "2.0",
"method": "update_order",
"id": 666,
"params":
    {
    "order_id": UUID,
    "любые другие поля Order'a которые изменились": ...,
    }
}

#####################

response = {"jsonrpc": "2.0",
"result":  "ok",
"id": 666}

FAQ

  • Зачем нужна концепция deposit/withdrawal?

Нужно понимать, чем биржа (Exchange) отличается от обменника (Swap).

Swap не нужны такие понятия, ибо он ничего не эмитирует. Он просто меняет монеты, забирая комиссию и не имеет такой сущности как дериватив (фантик, ассет). Exchange они нужны как минимум для того, чтобы сделать быстро select * from orders where type='deposit', это пригодится для саппорта и бизнеса(аналитику будет нужна 100%). Потом будет проблематично поверх рабочей конструкции строить скрипты, которые выясняют что из транзакций депозит, что вывод, а что мусор. Проще добавить одно int-поле в БД сейчас.