D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Введение
В данной статье будут описаны новые функции для API-сервиса, написанного в предыдущей статье - https://xss.is/threads/127492/. Советую сначала прочитать предыдущую статью для полного понимания проекта.Также на базе одной из этих новых функций будет реализован отдельный софт в виде чекера мнемонических фраз криптовалюты Dash с проверкой баланса и автовыводом без использования каких-либо сторонних сервисов.
В чем смысл данного API-сервиса?
Некоторые могут задаться вопросом: зачем делать отдельный API-сервис, а потом на его основе чекер мнемонических фраз? Да, можно сделать сразу один софт в виде чекера, но суть написания отдельного API-сервиса заключается в том, чтобы создать удобный, публичный инструмент, который можно поставить на сервер и на его основе разрабатывать множество других программ с использованием меньшего количества кода, а также предоставить доступ другим людям.Но можно же использовать сразу подключение по RPC к ноде.
Да, можно использовать сразу RPC в своих программах без дополнительной прокладки в виде API-сервиса. Однако API-сервис представляет собой, как минимум, готовый к использованию Swagger, в котором достаточно подставить свои данные без необходимости искать нужные команды в неудобных документациях, таких как документация Dash. Также в API будут присутствовать функции, которых нет по умолчанию в ноде. Одна из таких функций будет рассмотрена ниже.Примером подобных API-сервисов могут быть API Etherscan, TONCenter, Blockchair API, но у всех этих сервисов есть ограничения на количество запросов. В написанном мной API таких ограничений нет.
Функция проверки мнемонических фраз на баланс
Эта функция необходима для реализации чекера мнемонических фраз. Основная логика функции должна заключаться в получении из мнемонической фразы данных обо всех адресах, привязанных к этой фразе. Чтобы это сделать, я нашел два решения: медленное, но рабочее и легкое в реализации, а также быстрое, но более сложное в реализации. Сначала будет рассмотрен легкий способ.P.S. Сразу хочу отметить, что мнемоническая фраза и seed-фраза — это разные вещи. Мнемоническая фраза представляет собой набор слов, а seed-фраза — это байтовое представление мнемонической фразы. Это важно учитывать при разработке.
Первый вариант
Суть способа заключается в импорте мнемонической фразы в локальный кошелек DashCore. Однако данный метод не подходит для использования в чекере.Почему такой метод не подойдет?
- Для каждой мнемонической фразы придется создавать локальный кошелек. При работе с сотнями мнемонических фраз это станет плохой идеей.
- Скорость. После импорта мнемонической фразы в кошелек обязательно нужно выполнять рескан, чтобы увидеть адреса и балансы импортированной фразы. Без рескана в кошельке ничего не отобразится. Это не было бы столь критично, если бы рескан занимал 1-2 секунды, но на практике он занимает около 3-5 минут.
Недоработка DashCore
Хочу упомянуть неприятную недоработку в DashCore. Проблема в том, что после рескана импортированной мнемонической фразы или приватного ключа информация об импортированных данных не сохраняется в DashCore. При повторном импорте тех же самых данных без выполнения рескана данные будут пустыми. Это означает, что рескан нужно всегда выполнять заново, даже если ранее те же самые данные уже были добавлены.Хотя этот метод не подходит для чекера, я считаю нужным показать, как он работает, так как функция может пригодиться в других случаях.
Логика действий
- Создание кошелька с включённым параметром blank.
- Обновление кошелька до формата HD (может поддерживать импорт мнемонических фраз).
- Импорт мнемонической фразы.
- Проверка адресов в кошельке через команду listaddressgroupings.
Особенности создания кошелька
Для начала стоит разобраться, как создавать кошельки. В предыдущей статье уже было показано, как это делать, и в коде уже есть соответствующая функция. Однако она не подойдёт, так как создание нужно производить с дополнительными параметрами. Если точнее, следует включить параметр Blank. Этот параметр означает, что кошелёк будет создан полностью пустым (без адресов, ключей и структуры HD).Для чего нужен параметр Blank?
Если не указывать параметр Blank при создании кошелька, то по умолчанию кошелёк будет HD. Однако я не нашёл команды для импортирования мнемонической фразы в такой кошелёк. Поэтому сначала кошелёк нужно создать без структуры HD, а затем с помощью команды upgradetohd преобразовать его в HD. При выполнении этой команды как раз и можно указать мнемоническую фразу.Создание кошелька и импорт мнемонической фразы
Теперь перейдём к коду. Первой на очереди будет функция для создания кошелька и импортирования мнемонической фразы.Python: Скопировать в буфер обмена
Код:
@router.get("/api/v1/import_mnemonic_phrase", tags=["DASH WALLET"])
async def import_mnemonic_phrase(
mnemonic_phrase: str = Query(..., description="Мнемоническая фраза"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
check_uuid(user_uuid, db)
# Проверка количества кошельков для данного пользователя
wallet_count = db.query(DWallet).filter(DWallet.user_unique_id == user_uuid).count()
if wallet_count >= 5:
return {"error": "Превышен лимит кошельков. У пользователя не может быть более 5 кошельков."}
# Генерация случайного имени кошелька
wallet_name = "dhdwallet_" + secrets.token_hex(8) # Генерация случайного имени кошелька
# Создание нового кошелька через отправку команды RCP в ноду
method = "createwallet"
params = [wallet_name, False, True] # Имя кошелька
rpc_call(method, params) # Вызов метода для отправки команды RCP в ноду
# Добавление кошелька в базу данных
new_wallet = DWallet(wallet_name=wallet_name, user_unique_id=user_uuid)
db.add(new_wallet)
db.commit()
db.refresh(new_wallet)
# Импорт мнемонической фразы в новый кошелек
method = "upgradetohd"
params = [mnemonic_phrase, ""]
result = rpc_call(method, params, wallet_name=wallet_name)
# Проверка на ошибки
if result.get("error"):
raise HTTPException(status_code=400, detail=result["error"])
return {"status": "success",
"wallet_name": wallet_name,
"data": result["result"],}
Python: Скопировать в буфер обмена
params = [wallet_name, False, True]
Blank — это третий параметр, и чтобы кошелёк создавался пустым, его значение должно быть True.
Проверка адресов и баланса кошелька
Далее представлена функция для проверки адресов кошелька.Python: Скопировать в буфер обмена
Код:
@router.get("/api/v1/address_list", tags=["DASH WALLET"])
async def address_list(
wallet_name: str = Query(..., description="Название кошелька"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID
check_uuid(user_uuid, db)
# Проверка, принадлежит ли кошелек указанному пользователю
wallet = db.query(DWallet).filter(DWallet.wallet_name == wallet_name, DWallet.user_unique_id == user_uuid).first()
if not wallet:
return {"error": "Кошелек с указанным именем не найден у пользователя с данным UUID."}
method = "listaddressgroupings"
result = rpc_call(method, wallet_name=wallet_name)
return result["result"]
Результат
Вызов функции создания кошелька и импортирования в него мнемонической фразы в ответ выдаёт просто True. Поэтому сразу перехожу к вызову проверки адресов кошелька, и вот что получаем:Второй вариант
Теперь рассмотрим второй вариант, который и будет использован в чекере. Этот способ достаточно быстрый, и 50 мнемонических фраз я проверил за 2-3 секунды. Суть его заключается в ручной генерации адресов на основе мнемонической фразы.Как будет устроена генерация адресов?
- Преобразование мнемонической фразы в seed.
- Генерация корневого ключа из seed (приватный ключ именно от seed-фразы).
- Генерация публичных ключей из корневого ключа с использованием деривации.
- Несколько этапов хеширования полученного после деривации ключа, чтобы получить адреса (то есть через деривацию проходят именно ключи, а затем, используя хеширование из ключей, будем получать адреса).
Для чего нужна деривация?
Прежде всего, нужно понимать, что мнемоническая фраза — это не просто доступ к конкретному адресу. Мнемоническая фраза — это доступ к миллионам адресов, и деривация как раз позволяет получить эти миллионы адресов путём добавления конкретных значений к корневому ключу. Именно это и называется деривацией.Пример добавляемых данных при деривации
Данные, которые добавляются к корневому ключу, называются деривационным путём. Вот пример такого пути:Python: Скопировать в буфер обмена
m/44'/5'/0'/0/0
Значение чисел
- 44 — это стандарт для создания иерархии криптовалютных кошельков.
- 5 — это индекс криптовалюты (Dash — 5, BTC — 0, LTC — 2).
- 0 — это индекс аккаунта.
- Второй 0 — это индекс типа адреса (внешний, то есть тот, на который можно принимать платежи).
- Третий 0 — это индекс адреса (все адреса начинаются с индекса 0).
Для чего индексация адресов?
Ранее уже было сказано, что адресов может быть миллионы, более того, если я не ошибаюсь, их там 4 миллиарда для каждого типа, а типов — бесконечное дерево. Поэтому просто перебирать каждый для поиска нужного нет смысла. Существует индексация каждого адреса, и именно поэтому все кошельки выдают адреса по порядку, начиная с нулевого. Это необходимо для того, чтобы быстро перебрать адреса по индексу и найти нужные данные конкретных адресов.Для чего несколько этапов хеширования?
По сути, хеширование задумывалось как улучшенная защита данных и уникализация. Но на практике в данном случае оно нужно просто потому, что это стандарт BIP32/BIP44, чтобы получить в нашем случае адрес кошелька такой же, как если бы его выдал сторонний кошелёк.Этапы хеширования
- Хеширование публичного ключа с использованием алгоритмов SHA-256 и RIPEMD-160 после его деривации.
- В начало полученного хешированного ключа добавляется префикс \x4c, обозначающий, что это публичный ключ Dash.
- Полученный ключ с префиксом хешируется 2 раза с помощью SHA-256.
- Берём первые 4 байта от получившегося хеша — это контрольная сумма.
- Берём ключ, получившийся в пункте 2, и добавляем в конце контрольную сумму из пункта 4.
- Кодируем в Base58.
Дополнительно
Дополнительно будет реализовано получение приватного ключа из мнемонической фразы. Всё это будет сделано в одной функции, чтобы в дальнейшем была возможность с помощью приватных ключей осуществлять автовывод с кошелька.Как получить приватный ключ
Логика получения приватного ключа из мнемонической фразы схожа с получением публичного ключа и адреса, но всё же отличается.Отличия
- В отличие от публичного ключа, после деривации не нужно хешировать ключ в самом начале.
- К ключу после деривации добавляется префикс \x01, и не в начале, а в конце. Этот префикс означает, что ключ будет в сжатом формате.
- Затем к полученному ключу с префиксом в конце добавляется префикс в начале (\xCC). Этот префикс ставится именно для приватных ключей.
- Полученный ключ с префиксами хешируется 2 раза с помощью SHA-256.
- Берём первые 4 байта от получившегося хеша — это контрольная сумма.
- Берём ключ, получившийся в пункте 3, и добавляем в конце контрольную сумму из пункта 5.
- Кодируем в Base58.
Логика функции в целом
- Преобразование мнемонической фразы в seed.
- Генерация корневого ключа из seed (приватный ключ именно от seed-фразы).
- Внутри цикла получать публичные ключи, используя деривацию корневого ключа с изменением последнего значения в деривационном пути (индекс), то есть при каждом цикле добавлять +1 для получения, например, ста первых ключей.
- Несколько этапов хеширования полученного после деривации ключа для получения адреса.
- Внутри цикла получать приватные ключи, используя деривацию корневого ключа с изменением последнего значения в деривационном пути.
- Несколько этапов хеширования (но меньше, чем с публичным адресом) для получения приватного ключа в нужном формате.
- Отправка запроса на ноду для проверки баланса по адресу.
- Суммирование балансов всех адресов в один.
- Возвращать ответ с адресами, приватными ключами и балансом, а также общий баланс со всех адресов.
Реализация функции
С теорией покончено, и теперь можно рассмотреть саму функцию получения баланса по мнемонической фразе. Первое, что нужно сделать — это добавить импорты необходимых библиотек.Python: Скопировать в буфер обмена
Код:
import hashlib
import base58
from mnemonic import Mnemonic
from bip32utils import BIP32Key
Затем рассмотрим саму функцию.
Python: Скопировать в буфер обмена
Код:
@router.get("/api/v1/check_mnemonic", tags=["DASH"])
async def check_mnemonic(
mnemonic_phrase: str = Query(..., description="Мнемоническая фраза"),
start_index: int = Query(0, description="Начальный индекс адреса"),
amount_address: int = Query(100, description="Количество проверяемых адресов"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
check_uuid(user_uuid, db)
# Переменная для хранения общего баланса
total_balance = 0
# Список для хранения адресов и приватных ключей
addresses_with_keys = []
# Преобразуем мнемоническую фразу в seed (в байтовое представление)
seed = Mnemonic("english").to_seed(mnemonic_phrase)
# Генерация корневого ключа из seed
root_key = BIP32Key.fromEntropy(seed)
# Генерация указанного количества адресов
for index in range(start_index, start_index + amount_address):
# Деривация публичного ключа
pubkey = root_key.ChildKey(44 + 0x80000000).ChildKey(5 + 0x80000000).ChildKey(0 + 0x80000000).ChildKey(0).ChildKey(index).PublicKey()
# Хешируем публичный ключ с использованием алгоритмов SHA-256 и RIPEMD-160
ripemd160_hash = hashlib.new('ripemd160', hashlib.sha256(pubkey).digest()).digest()
# Добавляем к хешируемому публичному ключу \x4c что бы указать что это ключ для dash (BTC-\x00, LTC-\x30)
payload = b'\x4c' + ripemd160_hash
# Хэшируем payload (\x4c + хеш публичного ключа) с помощью SHA-256
first_sha256 = hashlib.sha256(payload).digest()
# Хэшируем first_sha256, то есть хешируем другой хеш, это делается для усиления защиты
second_sha256 = hashlib.sha256(first_sha256).digest()
# Берем первые 4 байта от получившегося хеша second_sha256, получается контрольная сумма
checksum = second_sha256[:4]
# Формируем итоговый адрес, добавляя контрольную сумму к payload (хеш публичного ключа + \x4c)
address_with_checksum = payload + checksum
# Кодируем адрес в формате Base58Check
dash_address = base58.b58encode(address_with_checksum).decode('utf-8')
# Деривация приватного ключа
private_key_bytes = root_key.ChildKey(44 + 0x80000000).ChildKey(5 + 0x80000000).ChildKey(0 + 0x80000000).ChildKey(0).ChildKey(index).PrivateKey()
# Добавляем байт 0x01 для того что бы указать что приватный ключ в сжатом формате
private_key_compressed = private_key_bytes + b'\x01'
# Добавляем к приватному ключу в начале \xCC что бы указать что это ключ для dash
private_payload = b'\xCC' + private_key_compressed # Префикс Dash приватного ключа
# Хэшируем private_payload с помощью SHA-256
private_first_sha256 = hashlib.sha256(private_payload).digest()
# Хэшируем полученный выше хэш
private_second_sha256 = hashlib.sha256( private_first_sha256).digest()
# Берем первые 4 байта от получившегося хеша private_second_sha256, получается контрольная сумма
checksum = private_second_sha256[:4]
# Формируем итоговый приватный ключ, добавляя контрольную сумму к private_payload
private_key_with_checksum = private_payload + checksum
# Кодируем адрес в формате Base58Check
private_key = base58.b58encode(private_key_with_checksum).decode('utf-8')
# Запрос баланса
method = "getaddressbalance"
params = [dash_address]
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), dict):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
# Получаем баланс
balance = result.get("result", {}).get("balance", 0)
# Суммируем баланс
total_balance += balance
# Добавляем данные в список
addresses_with_keys.append({
"address": dash_address,
"private_key": private_key,
"balance_satoshi": balance,
"balance": balance / 100000000
})
# Возвращаем общий баланс и список адресов с ключами
return {
"total_balance_satoshi": total_balance,
"total_balance": total_balance / 100000000,
"addresses": addresses_with_keys
}
Результат
Теперь рассмотрим использование данной функции через интерфейс документации FastAPI.Тут всё просто: указываем мнемонику, индекс адреса, с которого начнётся генерация, и количество адресов, которые мы должны получить. То есть, если указано 100, то получим 101 адрес. Нажимаем на кнопку — функция генерирует адреса, проверяет их баланс через RPC команду getaddressbalance, суммирует значения и выдает результат, как показано на скриншоте ниже.
Чекер
Теперь можно приступать к написанию чекера на основе этого API.Что будет делать чекер?
- Брать мнемонические фразы из txt (каждая строка — это отдельная мнемоническая фраза).
- Проверять каждую из фраз на баланс, отправляя запросы на написанное ранее API.
- Записывать в новый txt мнемоническую фразу, адрес, приватный ключ и найденный баланс на адресе (записывает только адреса с балансом больше нуля).
Реализация
Python: Скопировать в буфер обмена
Код:
import requests
# Параметры API
BASE_URL = "http://127.0.0.1:8000/api/v1/"
INPUT_FILE = "mnemonics.txt" # Файл с мнемоническими фразами
OUTPUT_FILE = "checked_mnemonics.txt" # Файл с результатами
START_INDEX = 0 # Начальный индекс кошелька
AMOUNT_ADDRESS = 10 # Количество адресов (конечный индекс)
UUID = "" # UUID для запроса
def api_request(action, params):
# Основная ссылка + метод
url = f"{BASE_URL}{action}"
try:
# Отправка запроса с параметрами переданными в функцию
response = requests.get(url, params=params)
return response.json()
except requests.RequestException as e:
print(f"Ошибка при выполнении запроса {action}: {e}")
return None
def process_mnemonics(input_file, output_file):
"""Читает мнемонические фразы из файла, проверяет баланс и записывает результаты."""
with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
for mnemonic in infile:
# Удаляет все лишние пробелы, табуляцию, перенос строки с начала и конца строки
mnemonic = mnemonic.strip()
# Запрос на проверку баланса
response = api_request("check_mnemonic", {
"mnemonic_phrase": mnemonic,
"start_index": START_INDEX,
"amount_address": AMOUNT_ADDRESS,
"user_uuid": UUID
})
# Если ответ от сервера не пустой
if response is not None:
# Достает из ответа адрес
addresses = response.get("addresses", [])
for addr_info in addresses:
# Достает из ответа баланс адреса который был получен выше
balance = addr_info.get("balance", 0)
# Если баланс больше нуля
if balance > 0:
dash_address = addr_info.get("address", "Неизвестный адрес")
private_key = addr_info.get("private_key", "Неизвестный ключ")
result = (
f"Мнемоника: {mnemonic}\n"
f"Адрес: {dash_address}\n"
f"Приватный ключ: {private_key}\n"
f"Баланс: {balance}\n"
f"====================================================================\n"
)
# Запись в файл
outfile.write(result)
print(result)
else:
print(f"Не удалось получить данные для мнемонической фразы: {mnemonic}")
if __name__ == "__main__":
process_mnemonics(INPUT_FILE, OUTPUT_FILE)
Результат работы софта
P.S. Нагенерировать мнемонические фразы для теста можно на этом сайте: BIP39 - Mnemonic Code
Чекер + автовывод
Также хочу показать отдельный вариант чекера с автоматическим выводом баланса, если он больше нуля. Решил сделать это отдельной версией, так как не всем может это пригодиться. Автовывод будет реализован также с помощью самописного API-сервиса.Как будет работать чекер?
- Берутся мнемонические фразы из txt (каждая строка — это отдельная мнемоническая фраза).
- Проверяется каждая из фраз на баланс, отправляя запросы на написанное ранее API.
- Записывается в новый txt мнемоническая фраза, адрес, приватный ключ и найденный баланс на адресе (записываются только адреса с балансом больше нуля).
- Отправляется адрес кошелька с балансом, адрес куда отправлять, 90% от найденной суммы, приватный ключ в функцию для отправки запросов на создание, подписание и отправку транзакции.
- В вызванной функции отправляются данные в функцию отправки запросов по порядку: сначала данные для создания транзакции, потом для подписания и для отправки в сеть.
Реализация
Теперь рассмотрим саму функцию. В коде я указал более подробные комментарии для лучшего понимания.Python: Скопировать в буфер обмена
Код:
import requests
# Параметры API
BASE_URL = "http://127.0.0.1:8000/api/v1/"
INPUT_FILE = "mnemonics.txt" # Файл с мнемоническими фразами
OUTPUT_FILE = "checked_mnemonics.txt" # Файл с результатами
START_INDEX = 0 # Начальный индекс кошелька
AMOUNT_ADDRESS = 10 # Количество адресов (конечный индекс)
UUID = "" # UUID для запроса
DESTINATION_ADDRESS = "" # Адрес получателя
def api_request(action, params):
# Основная ссылка + метод
url = f"{BASE_URL}{action}"
try:
# Отправка запроса с параметрами переданными в функцию
response = requests.get(url, params=params)
return response.json()
except requests.RequestException as e:
print(f"Ошибка при выполнении запроса {action}: {e}")
return None
# Проверка баланса и обработка мнемонических фраз
def process_mnemonics(input_file, output_file):
with open(input_file, "r", encoding="utf-8") as infile, open(output_file, "w", encoding="utf-8") as outfile:
for mnemonic in infile:
# Удаляет все лишние пробелы, табуляцию, перенос строки с начала и конца строки
mnemonic = mnemonic.strip()
# Запрос на проверку баланса
response = api_request("check_mnemonic", {
"mnemonic_phrase": mnemonic,
"start_index": START_INDEX,
"amount_address": AMOUNT_ADDRESS,
"user_uuid": UUID
})
# Если ответ от сервера не пустой
if response:
# Достает из ответа адрес
addresses = response.get("addresses", [])
for addr_info in addresses:
# Достает из ответа баланс адреса который был получен выше
balance = addr_info.get("balance", 0)
# Если баланс больше нуля
if balance > 0:
dash_address = addr_info.get("address", "Неизвестный адрес")
private_key = addr_info.get("private_key", "Неизвестный ключ")
result = (
f"Мнемоника: {mnemonic}\n"
f"Адрес: {dash_address}\n"
f"Приватный ключ: {private_key}\n"
f"Баланс: {balance}\n"
f"====================================================================\n"
)
# Запись в файл
outfile.write(result)
print(result)
# Вычисление 90% от баланса и преобразование в целое число потому что api принимает только int, т.к сумма не в Dash а в satoshi
amount_to_send = int(balance * 0.9 * 100000000) # Умножаем на 100000000, чтобы получить satoshi и преобразуем в int.
# Вызываем функцию для отправки запросов на создание, подписание и отправку транзакции
create_and_broadcast_transaction(
from_address=dash_address,
to_address=DESTINATION_ADDRESS,
amount=amount_to_send,
private_key=private_key
)
else:
print(f"Не удалось получить данные для мнемонической фразы: {mnemonic}")
def create_and_broadcast_transaction(from_address, to_address, amount, private_key):
# Создание транзакции.
# Отправляем данные в функцию отправки запроса.
# create_transaction_auto_fee это часть адреса куда отправлять запрос
create_response = api_request("create_transaction_auto_fee", {
"from_address": from_address,
"to_address": to_address,
"amount": amount,
"spend_change_to_address": from_address,
"user_uuid": UUID
})
if not create_response:
print("Ошибка при создании транзакции.")
return
# Вытаскивает из ответа созданную транзакцию.
raw_transaction = create_response.get("raw_transaction")
if not raw_transaction:
print("Не удалось получить raw_transaction из ответа.")
return
# Подпись транзакции.
# Отправляем полученную выше транзакцию и приватный ключ в функцию отправки запроса.
# В ответ получим подписанную транзакцию
sign_response = api_request("sign_transaction", {
"raw_transaction": raw_transaction,
"private_key": private_key,
"user_uuid": UUID
})
if not sign_response:
print("Ошибка при подписании транзакции.")
return
# Отправка транзакции.
# Отправляем подписанную транзакцию в функцию отправки запроса
broadcast_response = api_request("broadcast_transaction", {
"signed_raw_transaction": sign_response,
"user_uuid": UUID
})
if not broadcast_response:
print("Ошибка при отправке транзакции.")
return
print("Транзакция успешно отправлена:", broadcast_response)
if __name__ == "__main__":
process_mnemonics(INPUT_FILE, OUTPUT_FILE)
Результат
P.S. Транзакции проверяются и приходят на кошелек практически мгновенно, за это можно благодарить саму систему проверки транзакций в Dash через мастер ноды
На этом с разбором API методов для работы с мнемоническими фразами и написанием чекеров закончено, и можно перейти к новым второстепенным, но всё же полезным функциям для API сервиса.
Дополнительные функции для API сервиса
Ниже будут приведены дополнительные функции, которые были добавлены в API сервис. Так или иначе, каждая из этих функций будет полезна при работе с API как инструментом для написания других проектов, работающих с криптовалютой.Генератор QR кодов с адресами
В дальнейшем, на основе API сервиса, планируется разработать платежную систему, и было решено сразу добавить одну из несложных функций, которая для этого пригодится, а именно: генератор QR кодов с адресами кошельков. Сделать генератор несложно, так как существует библиотека qrcode, благодаря которой можно реализовать данную функцию в пару строк.Python: Скопировать в буфер обмена
Код:
@router.get("/api/v1/qr_generate", tags=["UNIVERSAL"])
async def qr_generate(
address: str = Query(..., description="Адрес кошелька"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
check_uuid(user_uuid, db)
# Создание объекта QR-кода
QRcode = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
# Добавление текста в QR-код
QRcode.add_data(address)
# Генерация QR-кода
QRcode.make()
# Цвета QR-кода
qr_color = '#FA5252' # Основной цвет
back_color = "#262831" # Цвет фона
# Генерация изображения QR-кода
QRimg = QRcode.make_image(fill_color=qr_color, back_color=back_color).convert('RGB')
# Сохранение QR-кода в объект bytesIO
img_bytes = io.BytesIO()
QRimg.save(img_bytes, format='PNG')
# Конвертация изображения в base64
img_base64 = base64.b64encode(img_bytes.getvalue()).decode('utf-8')
return img_base64
Результат
Вот что мы получаем, отправив адрес на этот маршрут в API.Это QR-код в виде base64, именно таким образом его можно передать на веб-страницу и отобразить. Но чтобы не писать свой сайт для проверки, можно воспользоваться этим сайтом для отображения: Base64 to PNG | Image | Base64 Decode | Base64 Converter | Base64
При проверке QR-кода с телефона все прекрасно определяется.
Получение списка всех кошельков DashCore
В функционале API есть возможность создавать локальные кошельки в DashCore (в том числе и при импортировании мнемонических фраз). Каждый из кошельков привязывается к конкретному пользователю, и для удобства была реализована функция для получения списка всех кошельков пользователя.Python: Скопировать в буфер обмена
Код:
@router.get("/api/v1/wallets_list", tags=["DASH WALLET"])
async def wallets_list(
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверяем, существуют ли кошельки с этим UUID
wallets = db.query(DWallet).filter(DWallet.user_unique_id == user_uuid).all()
# Если кошельков нет, возвращаем ошибку
if not wallets:
raise HTTPException(
status_code=404,
detail=f"Кошельки для пользователя с UUID {user_uuid} не найдены."
)
# Возвращаем список названий кошельков
return [wallet.wallet_name for wallet in wallets]
Результат
Рассмотрим использование этой функции через интерфейс:Что получилось по итогу написания статьи?
По итогу написания статьи мы получили полноценные чекеры, использующие собственное API с собственной нодой. Благодаря этому можно не зависеть от сторонних сервисов. А также несколько дополнительных функций для применения не только в чекерах.Вывод
Думаю, на данном этапе добавление функционала конкретно для Dash завершено. Скорее всего, дальше я буду добавлять функционал для других валют, например, LTC или ETH. Из сложностей в написании методов и чекеров в текущей статье хочу отметить только работу с получением адресов и приватных ключей. Пришлось перерыть немало интернета и прочитать много документаций, чтобы разобраться в устройстве кошельков.Объяснить это просто и понятно было также сложно в статье, так что если что-то непонятно — пишите в комментариях.
Статья в виде документа - https://docs.google.com/document/d/1d078mpylC_qHIlTJHIsMIMYBRjYDMWEXGU42W2YIZYY/
Исходный код проекта на GitHub - https://github.com/overlordgamedev/Hooli-Crypto-API
Сделано OverlordGameDev специально для форума XSS.IS