MultiCurrencyWallet icon indicating copy to clipboard operation
MultiCurrencyWallet copied to clipboard

SOIP 2693: Transaction Ledger Service. database-like api for "requests", "multisig", "2fa" etc

Open vladiuz1 opened this issue 4 years ago • 18 comments

Problem

  • Buggy frontend.
  • IPFS unreliable, and not protected against flood by definition.
  • Saving of initiated transactions.
  • Dependancy of frontend on blockchain nodes. In practice its a bad system.

Task

Make a Centralized but Open-source Transaction Ledger Service.

  1. Keep all transaction in one big ledger on server-side.
  2. Transactions in all its stages, from initiation to completion.
  3. Group transaction by its logic (atomic swaps?)
  4. Make it opensource, so anyone can put up its transaction ledger service.
  5. Separate transaction signing (done on frontend) from blockchain ledgers, blockchain connections, and blockchain node messaging, make 1 protocol to talk to server rather than connecting to different blockchains from frontend.

Purpose

It should solve the following:

  • fix the reliability of service problem
  • fix the size of scripts needed to load on frontend

Area

backend and frontend

Estimate time of implementation

6 months

Additional data

vladiuz1 avatar Apr 20 '20 09:04 vladiuz1

Transaction Ledger Daemon

It should have very specific functionality.

It should keep record of all transactions by all wallets setup @ swaponline.

Its likely to be separated from swalponline process, however it should-be atomic-swap aware as it shuould know that some transactions in the ledger are grouped into atomic swaps. That is a few transactions may be grouped into 1 atomic swap transaction sequence.

The ledger daemon however has nothing to do with watching over execution of the transactions or making sure these transactions are confirmed.

It should only publish its APIs in order to save the state of transaction, list needed transactions, calculate balance for a wallet or list of wallets, and have a database-like api with limited functionality.

  • LIST(filter-args) - list transactions that match filter
  • SUB(filter-args) - subscribet to messages that match filter
  • NEW(args) - create new transaction
  • CHANGE-STATE(state) - change state of trasaction
  • HIDE(transaction) - hide transaction
  • BALANCE(filter_args) - get balance for list of wallets

It should also have AUTH part to authenticate requests to the API.

When the request is made by frontend, they should be signed by frontend masterkey library, and the ledger daemon needs to know how to authorise such requests.

For example:

I am initiating a bitcoin transaction for wallet 3, and its a multisig wallet. Once I click "sign", the transaction with all its data is sent to ledger daemon. The transaction is not signed yet, as its only "new" or "initiated"... However the ledger daemon must know that the initiator is indeed the owner of the wallet, it should know that the details of the transaction is signed by the owner and it should understand how to verify bitcoin signatures. In this particular case the authentication will represent verifying signature using bitcoin cryptography with bitcoin private key for this particular wallet.

The signature is done on the md5 of the json detailing the initiated transaction

{'from': '3jhfgjGFKjhgq34vdfkj34hg34545df65skg', to: '14vdfkj34hg34545df65skgjhfgjGFKjhgq3', 'type': 'btc', 'amount':0.23 }

there is no payload yet, which would contain raw bitcoin bytecode transaction with or without signature as this is only an initiated transaction, not confirmed, however it needs to be saved into the ledger before signing and broadcasting to network takes place.

The ledger must also keep wallet addresses, and understand what profile each multisig (and 2fa) wallet belongs.

The profiles are identified by public part of masterkey (derived from the 12 word mnemonic).

The interaction with the daemon must be proxied by a messaging service a-la rabbitmq.

So that every request to the ledger daemon is a sync or async message to daemon.

Every transaction on a ledger also triggers a message to the rabbitmq into its relevant pubsub channels.

For example, you may subscribe to system messages for all new transactions that concern a particular wallet.

vladiuz1 avatar Apr 20 '20 10:04 vladiuz1

Blockchain Daemon

Another daemon (or daemons) that will run on top of transactoin ledger, and are most likely to reside on the same it infrastructure are blockchain polling/broadcasting daemons.

Multiple instances of these daemons are subscribed to pubsub messaging server of transaction daemon and they are resposible for

  1. broadcasting signed transactions from ledger daemon to relevant blockchains. They may do so using different transports. like bitpay api, direct blockchain node connections, and you can make as many of them as possible.
  2. they update the state of already broadcast transactions, so once the state of a transaction on a blockchain changes they update the relevant state on the ledger. And every update as we know trigger a messaging queue, and services that are subscribed to such triggers make relevant actions after such state changes.

vladiuz1 avatar Apr 20 '20 10:04 vladiuz1

Same infrustructure

All services will be run as dokcer containers, so that they can be easily managed and scaled. Minimum config approach must be taken

All services need to use the same auth algorithms and similar api.

Other services may include:

  • 2FA daemon
  • Orderbook daemon
  • Swap assistant daemon
  • Exchange API daemon

etc

such plug&play infrustructure on a centralized but scaleable messaging system must be designed so we can plug in services as we go.

vladiuz1 avatar Apr 20 '20 11:04 vladiuz1

TODO 1 (important)

  • [x] keep 2fa wallet (my 2fa wallet is restored when i entered 12 words)
  • [x] keep multisig wallets
  • [ ] show 1/2 2fa in history
  • [x] show 1/2 multisig tx in history
  • [ ] eth and erc20 final balance appears in history
  • [ ] critical wallet functionallity does not affect in case when our server is down (i can send and recieve payments)
  • [x] It should also have AUTH part to authenticate requests to the API.

TODO 2 (delayed)

  • [ ] encryption
  • [ ] publish to open source
  • [ ] documentation
  • [ ] rabbitmq
  • [ ] complate API

noxonsu avatar Apr 20 '20 21:04 noxonsu

связано https://github.com/swaponline/MultiCurrencyWallet/issues/2631

noxonsu avatar Apr 21 '20 01:04 noxonsu

Я думаю надо сначала утвердить API для этого сервиса, потом сделать brainstorm по получившемуся со всеми участниками процесса чтобы выявить возможные проблемы и решения. И как только АПИ утрясен - по полученной спецификации приступать и паралельно писать тесты.

vladiuz1 avatar Apr 21 '20 05:04 vladiuz1

@noxonsu

Кто-то работает над описанием этого АПИ для взаимодействия с сервером?

Надо описать

  • наборы полей в транзах
  • состояния
  • методы
  • транспорт

И так далее

vladiuz1 avatar Apr 29 '20 05:04 vladiuz1

@vladiuz1 да, сегодня как раз прорабатываем мультисиг

noxonsu avatar Apr 30 '20 04:04 noxonsu

какой прогресс по описанию

vladiuz1 avatar May 13 '20 05:05 vladiuz1

@vladiuz1 В данный момент у нас есть три сервиса (бекэенда)

  1. Инвойсы
  2. 2FA (SMS подтверждение btc)
  3. Сервер (хранилище) транзакций multisig

Все сервисы написаны на JS (Node). В качестве базы данных используется sql-lite

Описал внутреннее устройство сервисов в схеме

swaponline

Входные параметры роутеров чуть попозже опишу

shendel avatar May 14 '20 10:05 shendel

https://github.com/swaponline/MultiCurrencyWallet/issues/2675#issuecomment-616828242 Это тоже сюда. Проблема спама К архитектуре нашего сервиса не подходят классические подходы защиты от спама, из-за того, что он самодостаточный (без бек-енда и доступа в интернет, условно, может работать) Сложно идентифицировать клиента (и авторизировать) в классическом понимании этого слова - нельзя уходить от архитектуры анонимности и независимости (кос итд) При реализации сервисов исходил из правила "кто сильнее" (draft - есть понимание, реализовано не до конца). Во всех запросах, которые данные записывают в сервисы. Со стороны клиента используется активный кошелек BTC. Передаваемые параметры на сервер подписывает активным ключом. Расчет на вычислительную мощность. Перед отправкой запроса на сервер "плохишу" нужно потратить свои вычислительные мощности на генерацию приватника, формирование кошелька, формирование запроса, и подпись этого запроса. На стороне бек-эенда на сверку подписи тратиться меньше флопсов. Исходя из этого можно разработать перманентную защиту, применив прогрессивный тайм-аут (каждый следующий запрос должен прийти не раньше чем Время*Порядковый номер запроса). В случае злоупотребления - отсеивать запросы от этого кошелька

shendel avatar May 14 '20 11:05 shendel

@shendel

Спасибо за ответ

И прошу прощения что долго не отвечал - нет уведомлений

Посмотрел на предложенную архитектуру

Она видно что сделано с большим знанием системы

Но есть очевидные недостатки

Транзакции и инвойсы выделены в отдельные сущности, не учтено тот факт что транзакция в нашем понимании может состоять из нескольких де-факто блокчейн транзакий (swap?)

Я считаю что наш ledger нужно хранить в одном месте и там же отслеживать статус транзакций

Во-первых надо четко определить что такое транзакция на нашем леджере и что она включает

По этому предлагаю ввести 3 понятия

1. Document (документ)

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

{
  created_by: '0x73246273468823746', // profile hash of initiator of document
  created_at: 1590563883, // the time when the document was created
  invoice: { // У этого документа есть инвойс с которого он  наверно и начался
     symbol: "ETH", // символ
     from: "0xf0e67e547b3c2ef5b81889d51976864cf636b678", // от кого
     to: "0x0c0156540cab36ffeed3898f476003d59126f12a", // кому
     net: "mainnet", // сетка, ропстен?
     paid: false, // оплачен?
     cancelled: true, // отменен?
     amount: 100.0,  // сумма    
     message:  "Привет помнишь ты мне был должен?",
  },
  comments: { // пометки транзакции у себя в леджере
    "0x73246273468823746": "Ваня должен мне 100 эфиров",
    "0x4688abf46fd27aa3ee": "Забудь - я ничего не должен",
  },
  transactions: [
    ... // transactions associated with this document
  ]
}

2. Blockchain transaction (блокчейн транзакция)

Это транзакция (подписанная, или не подписанная, или частично подписанная) в формате блокчейна. Для каждого блокчейна транзакция может иметь разные наборы полей, адресов, может иметь одну или несколько подписей, но важно понимать что эта сущность - это одна выделенная из возможной серии транзакций которая непосредственна отражена в блокчейне, имеет blockchain-specific набор данных, статусы (подтверждения и так далее).

Транзакция может например не иметь tx id в блокчейне так как она не подписана (если блокчейн не имеет детерменированных tx id).

Пример транзакции в эфире например может быть такая запись

{
  type: "eth_transfer", // наш внутренний тип транзы
  block: 10146719,
  stamp: 1590563883, // время подтверждения
  id: 0xffa13e5e07f570175467be1b5a51c34e77818e1fe79edf202afa8ea8cc8b05f3, // хэш транзы
  confirmed: true, // считается ли транза подтвержденной (это уже нам решать когда ее считать подтвержденной)
  from: "0xf0e67e547b3c2ef5b81889d51976864cf636b678", // от кого
  to: "0x0c0156540cab36ffeed3898f476003d59126f12a", // кому
  amount: 0.00001, // сумма собственно
  fee: 0.00083853005991, // сколько из этого - фи
  gas_price: 0.000000039930002853,
  gas_used: 21000,
  raw: "0x4254837654983654754936845...", // байткод транзы самой вместе с сигнатурами если есть.
  signatures: ["0xf0e67e547b3c2ef5b81889d51976864cf636b678"]
}

Набор полей содержащих детали транзакции для каждого типа транзакции будет разным. Например перевод токенов существенно отличается от перевода биткойнов ибо там имеет место комиссия, которая берется в другой валюте, которую тоже нужно учитывать.

3. Ledger record (проводка - запись в бухгалтерском журнале)

Проводка - это запись бухгалтерского журнала которая непосредственно отражает изменение баланса и имеет очень простой вид

  {
   stamp: 1590563883, // время транзы
   wallet:  "1KbNEWN3DrFP4MLnAkBZocWBvCzZgBvhQX", // кошелек пользователя
   account: "wallet/balances", // внутренне название счета на который записывается сальдо
   symbol: "BTC", // символ валюты
   amount: -2.324 // кол-во бтц списанное с кошелька пользователя
  }

На каждый документ, если он имеет статус завершившейся транзакции - должно быть как минимум 2 проводки, ибо как мы знаем сумма дебитов и сумма кредитов в бухучете всегда равны. В нашем случае это будет 0 так как дебит мы будем вводить со знаком + а кредит со знаком -.

На самом деле в реальной жизни скорее всего на каждую завершенную транзакцию будет несколько проводок.

[
  {
   stamp: 1590563883, // время транзы
   wallet:  "1KbNEWN3DrFP4MLnAkBZocWBvCzZgBvhQX", // кошелек пользователя
   account: "wallet/balances", // внутренне название счета на который записывается сальдо
   symbol: "BTC", // символ валюты
   amount: -2.324 // кол-во бтц списанное с кошелька пользователя
  },
  {
   stamp: 1590563883,
   wallet:  "38u1srayb1oybVB43UWKBJsrwJbdHGtPx2", // кошелек майнера который получил комиссию за подписынный блок. 
   account: "wallet/balances", // внутреннее название счета в отчетах
   symbol: "BTC", 
   amount: 0.3224 // комиссия за подписанный блок которую заплатил инициатор транзы
  },
  {
   stamp: 1590563883,
   wallet:  "1PsMq4RdRWfRtnqojh6LNxjeeucQ8HPbiR", // кошелек получателя
   account: "wallet/balances", // внутреннее название счета в отчетах
   symbol: "BTC", 
   amount: 2.0 // то что получил адресат перевода
  }
]

Такая система проводок позволит вести учет движений денег, составлять отчеты, считать балансы в одном месте. Она универсальна для подсчета балансов и если в какой-то момент мы сделаем плагин 2фа со встроенной комиссией мы будем проводить такую транзакцию по журналу таким образом:

[
  {
   stamp: 1590563883, // время транзы
   wallet:  "1KbNEWN3DrFP4MLnAkBZocWBvCzZgBvhQX", // кошелек пользователя
   account: "wallet/balances", // внутренне название счета на который записывается сальдо
   symbol: "BTC", // символ валюты
   amount: -2.324 // кол-во бтц списанное с кошелька пользователя
  },
  {
   stamp: 1590563883,
   wallet:  "3QbbKpiRwDf7L9enQBhu3aqGq8T5FkqKAN", // наш кошелек на который начислена комиссия за подпись 2фа. 
   account: "wallet/balances", // внутреннее название счета в отчетах
   symbol: "BTC", 
   amount: 0.1 // комиссия которую мы получили
  },
  {
   stamp: 1590563883,
   wallet:  "38u1srayb1oybVB43UWKBJsrwJbdHGtPx2", // кошелек майнера который получил комиссию за подписынный блок. 
   account: "wallet/balances", // внутреннее название счета в отчетах
   symbol: "BTC", 
   amount: 0.1224 // комиссия за подписанный блок которую заплатил инициатор транзы
  },
  {
   stamp: 1590563883,
   wallet:  "1PsMq4RdRWfRtnqojh6LNxjeeucQ8HPbiR", // кошелек получателя
   account: "wallet/balances", // внутреннее название счета в отчетах
   symbol: "BTC", 
   amount: 2.0 // то что получил адресат перевода
  }
]

Время (stamp) будет отражено на тот момент, на который эти изменения балансов были подтверждены блокчейном.

Например при атомарном свопе даты каждой проводки могут отличаться так как балансы в каждом из блокчейнов будут меняться в разное время.

Проводки по идее являются производными блокчейн транзакций, поэтому логично их хранить внутри подтвержденных или не подтвержденных транзакций.

Нередко в бухгалтерском учете проводки имеют статус. ТО есть они могут быть подготовленными, удаленными, проведенными. Балансы меняют только проведенный проводки.

В этом на самом деле проводки очень похожи на то как работает блокчейн.

В проводках было-бы полезно указывать профиль пользователя - владельца кошелька (чтобы например посчитать балансы всех кошельков пользователей). Но это нельзя делать так как для некоторых кошельков (p2sh, смартконтракты, мультисиги) мы можем не знать владельцев, или их может быть несколько. Кроме этого пользователи могут добавлять к себе в профиль кошельки, которые "watch-only" и возможно не будут влиять на суммарный баланс.

поля stamp, symbol можно заимствовать из родительской блокчейн транзакции для проводки дабы избежать дублирования данных.

Пример документа

В общем наша база будет состоять из документов каждый из которых имеет метаданные плюс список блокчейн транзакций, и каждая из блокчейн транзакций будет иметь список проводок, которые собственно и меняют балансы кошельков в нашей системе.

Вот ниже привожу документ содержащий пересылку токена usdt на внешний кошелек

{
  created_by: "0xdc6768229be7c72e837242cf4d", // profile hash of initiator of document
  created_at: 1590563883, // the time when the document was created
  updated_at: 1590586130, // время последнего обновления документа
  type: "eth_token_transfer", // тип документа
  comments: { // пометки транзакции у себя в леджере
    "0xdc6768229be7c72e837242cf4d": "Перевод дедушке в деревню", // коментарий транзы во фронтенде пользователя 0xdc6768229be7c72e837242cf4d
  },
  transactions: [
    {
      type: null, // наш внутренний тип транзакции, если документ состоит из нескольких разных транзакций (swap?)
      net: "mainnet", // сетка, ропстен?
      block: 10147905,
      stamp: 1590564283, // время подтверждения
      id: 0xbcf483ca51b4f034a2bffe9cee29be7c72e837242cf4ddc67682be70ed93a159, // хэш транзы
      confirmed: true, // считается ли транза подтвержденной (это уже нам решать когда ее считать подтвержденной)
      from: "0x4c171979f638815ec0f399b37e7f2203e311b493", // от кого
      to: "0xdac17f958d2ee523a2206206994597c13d831ec7", // кому
      amount: 0.0, // сумма собственно
      fee: 0.00148309205087, // сколько из этого - фи
      gas_price: 0.000000036000001235,
      gas_used: 41,197,
      raw: "0xed4254f8ab3765498aa365475fbccd4936845...", // байткод транзы самой вместе с сигнатурами если есть.
      erc20: { // эта транза была  переводом erc20, поэтому есть доп метаданные
        symbol: "USDT",
        token_name: "Tether USD",
        from: "0x4c171979f638815ec0f399b37e7f2203e311b493",
        to: "0x145129ecb4dfacb80004a0db4a9ac2569636b3dc",
        amount: 3918.0,
     },
     signatures: ["0xf0e67e547b3c2ef5b81889d51976864cf636b678"], // ключом каких кошельков подписана транза. Для выборки.
     journal: [ // проводки в нашем журнале
      {
        wallet:  "0x4c171979f638815ec0f399b37e7f2203e311b493", // кошелек отправитель
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "USDT", 
        amount: -3918.0 // сумма в usdt
      },
      {
        wallet:  "0x145129ecb4dfacb80004a0db4a9ac2569636b3dc", // кошелек получателя токенов. 
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "USDT", 
        amount: 3918.0 // сумма в usdt
      },
      {
        wallet:  "0x4c171979f638815ec0f399b37e7f2203e311b493", // кошелек плательщика комиссии. 
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "ETH", 
        amount: -0.00148309205087 // сумма коммиссии в eth
      },    
      {
        wallet:  "0xea674fdde714fd979de3edf0f56aa9716b898ec8", // кошелек получателя комиссии - майнера. 
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "ETH", 
        amount: 0.00148309205087 // сумма коммиссии в eth
      }
    ]
   }
 ]
}

Коментарии о документах

Надо сказать разбивать информацию о таких сущностях будет очень удобно - визуально можно легко просматривать любой документ и такая структура дает гибкость на нескольких уровнях.

Во-первых гибкость в структуре данных и создании неограниченного кол-ва типов документов, добавление к ним новых полей.

Во-вторых позволит легко обогащать архитектуру новыми плагинами каждый из которых может добавлять свои типы документов и работать со своими метаданными документов.

Например для создания плагина с мультисигом мы можем просто дать такому плагину возможность добавить еще пару полей в документ -

  signatures_required: 2,
  signatories: ['walet','wallet','wallet']

со списком кошельков, чья подпись требуется для подписания. функционал при этом вынесется в отдельный модуль, который может быть включен или выключен.

Кроме этого мы сразу же можем объединить свои собственные метаданные с публичным blockchain данными, и поженить их в одной базе данных.

Мы просто для каждой транзакции блокчейна создадим документ в таком удобоваримом виде, и он с одной стороны будет иметь метаданные о транзакциях специфичных конкретному блокчейну, с другой стороны сразу же будет легко встраиваемый в наши балансы.

Wallets

Сервер должен знать о том какому профилю принадлежат какие кошельки.

Вернее не то чтобы принадлежат, а какому профилю какие кошельки интересны.

Эта привязка по идее должна быть в отдельной сущности данных.

profile -> wallet

Эта привязка интересна не только для того чтобы знать на стороне сервера какому профилю принадлежит кошелек (в большинстве случаев это можно вычислить алгоритмически зная публичные ключи), но и для того чтобы восстанавливать список отслеживаемых кошельков профиля.

Как Вы помните профиль - это лишь пара мастер ключей - приватный и публичный, все остальные кошельки - это деривативы мастер ключей, вычисляемые по bip32 (кажется). )))

Но при восстановлении профиля на новом девайсе пользователь не может помнить все адреса мультисигов, или адреса холодных кошельков, которых он отслеживал.

profile -> wallet привязка поможет нам с этим восстановлением

Я вижу это как индекс под названием wallets вот с такими (примерно) записями

[
  {
    wallet: '0x4c171979f638815ec0f399b37e7f2203e311b493',
    symbol: 'ETH', // символ  валюты (для эфира может быть несколько записей с одинаковым кошельком но разными валютами.
    type: 'derived', // тип, можно нагенерить других вариантов 'multisig', '2fa', 'eth_contract_multisig', итд. все зависит от плагинов
    nounce: 1, // для дериватив - индексное значение кошелька
    public_key: "0x68229be7dc6768268229be729be7c72e837242cf4d68229be7" , // публичный ключ кошелька
    profiles: ['0xdc6768229be7c72e837242cf4d'] // профили к которым добавлен кошель
  },
  {
    wallet: '0x3bfc20f0b9afcace800d73d2191166ff16540258',
    symbol: 'ETH', // символ  валюты (для эфира может быть несколько записей с одинаковым кошельком но разными валютами.
    type: 'eth_parity_multisig', // тип
    required: 2, // сколько нужно подписей
    addresses: ['0x0019A83251136740569c9D30ce6F22b1CB3ACC87','0x0035104354e028FC0728aFC6e48F8D80E2E8Af9D','0x00d594598e186f3AD98f02D6B8D7277130D3e8d8'], // адреса авторизованные для подписи
    profiles: ['0xdc6768229be7c72e837242cf4d','0xdc6768229be7c72e837242cf4d','0xdc6768229be7c72e837242cf4d'] // профили к которым добавлен кошель
  }

]

По сути это четвертая сущость нашей базы - wallet, но она вне индекса документов.

Как хранить данные

Только в json. Это самый удобный способ хранения, сегодня есть куча nosql, key/val баз данных.

Требования у нас могут быть следующие

  1. Возможность LOCK базы данных на запись, желательно per-document.
  2. Возможность вложенной индексации данных, включая в массивах. Чтобы можно было искать документы по такому запросу например: FIND ALL DOCS WHERE transactions[*].journal[*].wallet="0x0019A83251136740569c9D30ce6F22b1CB3ACC87"
  3. Возможность JOIN по индексам чтобы можно было посмотреть все транзакции по всем кошелькам, которые интересны одному из профилей.
  4. Возможность аггрегации с подсчетом сумм и кол-ва.
  5. Хранения данных в формате json со свободным кол-вом полей.
  6. Возможность масштабирования.

Почти все эти требования (кроме LOCK пожалуй) доступны в elasticsearch, возможно есть другие базы данных, которые удовлетворяют условиям. Не уверен что elasticsearch дает достаточный уровень надежности.

vladiuz1 avatar May 27 '20 08:05 vladiuz1

#2675 (comment) Это тоже сюда. Проблема спама К архитектуре нашего сервиса не подходят классические подходы защиты от спама, из-за того, что он самодостаточный (без бек-енда и доступа в интернет, условно, может работать) Сложно идентифицировать клиента (и авторизировать) в классическом понимании этого слова - нельзя уходить от архитектуры анонимности и независимости (кос итд) При реализации сервисов исходил из правила "кто сильнее" (draft - есть понимание, реализовано не до конца). Во всех запросах, которые данные записывают в сервисы. Со стороны клиента используется активный кошелек BTC. Передаваемые параметры на сервер подписывает активным ключом. Расчет на вычислительную мощность. Перед отправкой запроса на сервер "плохишу" нужно потратить свои вычислительные мощности на генерацию приватника, формирование кошелька, формирование запроса, и подпись этого запроса. На стороне бек-эенда на сверку подписи тратиться меньше флопсов. Исходя из этого можно разработать перманентную защиту, применив прогрессивный тайм-аут (каждый следующий запрос должен прийти не раньше чем Время*Порядковый номер запроса). В случае злоупотребления - отсеивать запросы от этого кошелька

Насчет защиты от спама - полностью согласен нужнен аутентификация на write транзакции - и limits, увеличение лимитов - за $$$ ;-) limits per wallet не будут работать - как дополнение нужно и per ip limits, при чем еще нужен ip blacklist. если кошель без балансов - выставлять invoice или swap order вообще нельзя.

vladiuz1 avatar May 27 '20 15:05 vladiuz1

https://habr.com/ru/post/254425/

Вот тут есть описание работы с полями json в postgress.

Надо бы нагенерить данных скажем из блокчейна btc такого вот формата

{
  created_by: "0xdc6768229be7c72e837242cf4d", // profile hash of initiator of document
  created_at: 1590563883, // the time when the document was created
  updated_at: 1590586130, // время последнего обновления документа
  type: "eth_token_transfer", // тип документа
  comments: { // пометки транзакции у себя в леджере
    "0xdc6768229be7c72e837242cf4d": "Перевод дедушке в деревню", // коментарий транзы во фронтенде пользователя 0xdc6768229be7c72e837242cf4d
  },
  transactions: [
    {
      type: null, // наш внутренний тип транзакции, если документ состоит из нескольких разных транзакций (swap?)
      net: "mainnet", // сетка, ропстен?
      block: 10147905,
      stamp: 1590564283, // время подтверждения
      id: 0xbcf483ca51b4f034a2bffe9cee29be7c72e837242cf4ddc67682be70ed93a159, // хэш транзы
      confirmed: true, // считается ли транза подтвержденной (это уже нам решать когда ее считать подтвержденной)
      from: ["0x4c171979f638815ec0f399b37e7f2203e311b493"], // от кого
      to: ["0xdac17f958d2ee523a2206206994597c13d831ec7"], // кому
      amount: 0.0, // сумма собственно
      fee: 0.00148309205087, // сколько из этого - фи
      gas_price: 0.000000036000001235,
      gas_used: 41,197,
      raw: "0xed4254f8ab3765498aa365475fbccd4936845...", // байткод транзы самой вместе с сигнатурами если есть.
      erc20: { // эта транза была  переводом erc20, поэтому есть доп метаданные
        symbol: "USDT",
        token_name: "Tether USD",
        from: "0x4c171979f638815ec0f399b37e7f2203e311b493",
        to: "0x145129ecb4dfacb80004a0db4a9ac2569636b3dc",
        amount: 3918.0,
     },
     signatures: ["0xf0e67e547b3c2ef5b81889d51976864cf636b678"], // ключом каких кошельков подписана транза. Для выборки.
     journal: [ // проводки в нашем журнале
      {
        wallet:  "0x4c171979f638815ec0f399b37e7f2203e311b493", // кошелек отправитель
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "USDT", 
        amount: -3918.0 // сумма в usdt
      },
      {
        wallet:  "0x145129ecb4dfacb80004a0db4a9ac2569636b3dc", // кошелек получателя токенов. 
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "USDT", 
        amount: 3918.0 // сумма в usdt
      },
      {
        wallet:  "0x4c171979f638815ec0f399b37e7f2203e311b493", // кошелек плательщика комиссии. 
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "ETH", 
        amount: -0.00148309205087 // сумма коммиссии в eth
      },    
      {
        wallet:  "0xea674fdde714fd979de3edf0f56aa9716b898ec8", // кошелек получателя комиссии - майнера. 
        account: "wallet/balances", // внутреннее название счета в отчетах
        symbol: "ETH", 
        amount: 0.00148309205087 // сумма коммиссии в eth
      }
    ]
   }
 ]
}

и попробовать написать наиболее частые запросы встречающееся у нас

а именно

список всех последних транзакций по нескольким кошелькам, список по одному кошельку, подсчет балансов индивидуального кошелька и по всем кошелькам. И посмотреть как быстро они выполняеются в постгрессе при учете что данные храняться в вышеописанном формате в json, при этом некоторые поля автоматически генерятся и индексируются посредством postgress

vladiuz1 avatar Jul 02 '20 14:07 vladiuz1

@Naggertooth сейчас как раз переносит данные о пользователях на постгре (#2950 ). следовательно таблица с данными пользователей может иметь вид

{
  created_by: "0xdc6768229be7c72e837242cf4d", // profile hash of initiator of document
  created_at: 1590563883, // the time when the document was created
  updated_at: 1590586130, // время последнего обновления документа
  type: "user_profile", // тип документа,
  domain: "wallet.wpmix.net" //домен с которого обращаемся
  data: {
   locale:"ru",
   wallets: {
    ethData: {address:"0x",balance:0},
    btcMultisigSMSData: {address:"",balance:0},
    //далее остальные кошельки 
   }
  }
}

noxonsu avatar Jul 02 '20 14:07 noxonsu

@Naggertooth сейчас как раз переносит данные о пользователях на постгре (#2950 ). следовательно таблица с данными пользователей может иметь вид

{
  created_by: "0xdc6768229be7c72e837242cf4d", // profile hash of initiator of document
  created_at: 1590563883, // the time when the document was created
  updated_at: 1590586130, // время последнего обновления документа
  type: "user_profile", // тип документа,
  domain: "wallet.wpmix.net" //домен с которого обращаемся
  data: {
   locale:"ru",
   wallets: {
    ethData: {address:"0x",balance:0},
    btcMultisigSMSData: {address:"",balance:0},
    //далее остальные кошельки 
   }
  }
}

верхняя структура имхо норм

замечания

  • domain: почему надо привязывать пользователя к домену? по идее если его ключ подходит он может его хранить на любом домене, потому если и хранить домен, то можно в контексте "domain": {"at_creation": "wallet.wpmix.net", "last_accessed_from": "swaponline.io"}

  • wallets

тут немного ограниченный формат

кошельки надо перечислять просто массивом имхо - например кошельков "ethData" может быть несколько и формат такой более правильный:

wallets: [
  {
    wallet: '0x4c171979f638815ec0f399b37e7f2203e311b493',
    symbol: 'ETH', // символ  валюты (для эфира может быть несколько записей с одинаковым кошельком но разными валютами.
    type: 'derived', // тип, можно нагенерить других вариантов 'multisig', '2fa', 'eth_contract_multisig', итд. все зависит от плагинов
    nounce: 1, // для дериватив - индексное значение кошелька
    public_key: "0x68229be7dc6768268229be729be7c72e837242cf4d68229be7" , // публичный ключ кошелька
  },
  {
    wallet: '0x3bfc20f0b9afcace800d73d2191166ff16540258',
    symbol: 'ETH', // символ  валюты (для эфира может быть несколько записей с одинаковым кошельком но разными валютами.
    type: 'eth_parity_multisig', // тип
    signatures_required: 2, // сколько нужно подписей
    signatories: ['0x0019A83251136740569c9D30ce6F22b1CB3ACC87','0x0035104354e028FC0728aFC6e48F8D80E2E8Af9D','0x00d594598e186f3AD98f02D6B8D7277130D3e8d8'], // адреса авторизованные для подписи
  }
]

Балансы я бы сразу вот не стал хранить в записи, сначала надо посчитать сколько занимает подсчет балансов на кошельках с огромным кол-вом транзакций

вполне может быть можно оптимизировать и будет мгновенно. тогда лучше не хранить дабы избедать инконсистенций данных

vladiuz1 avatar Jul 02 '20 14:07 vladiuz1

Да ну и идея состоит в том чтобы не переносить данные из firebase, а создать шаблон для транз биткойна, и по нему напольнить постгресс ВСЕМИ транзами биткойна.

Потом потестить скорость, и если все ок:

  • оптимизировать запросы,
  • обогатить нашими данными о пользаках

vladiuz1 avatar Jul 02 '20 14:07 vladiuz1

@Naggertooth отпиши статус по этой задаче плиз

noxonsu avatar Aug 20 '20 20:08 noxonsu