Skip to content

lifestreamy/odc-android-app

 
 

Repository files navigation

Open Digital Cash

Описание проекта

  • Мультимодульное приложение proof-of-concept для Android на Jetpack Compose
  • Используется технология блокчейна для реализации цифровой валюты
  • Происходит передача банкнот между устройствами пользователей (защищенными кошельками) по произвольному каналу связи (здесь — Bluetooth Classic) с применением 2-х собственных протоколов передачи данных (см. пункт Протоколы Передачи ниже)
  • Реализовано взаимодействие по HTTP с выделенным банковским сервером, который отвечает за регистрацию и авторизацию кошелька (пользователя), эмиссию банкнот, их выдачу/обмен на физические купюры.

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

Note

Это почти полностью переработанная, обновленная и дополненная версия старого приложения, которое можно найти здесь (в том числе произведена миграция с Fragments на Compose с новым дизайном, описанном в Figma)

Использованные технологии

  • Android Architecture Components (Lifecycle, Navigation, Room), Jetpack Compose, Dagger/Hilt, Kotlin, Coroutines, Flows, Datastore, Keystore, Retrofit, Bluetooth, Serialization, Gradle (Multi-module, Included Build, Convention Plugins, Version Catalogs, Build Variants), Git ;
  • Testing: Junit5

UI (Compose)

Home Settings History Wallet details
Light Home_light Settings_light History_light Wallet_details_light
Dark Home_dark Settings_dark History_dark Wallet_details_dark

Демонстрация работы приложения

Демонстрация работы приложения (запись экрана), в том числе 2-х splash-screen (системного android и внутреннего в приложении), анимаций.

Note

Во время передачи банкнот устройство взаимодействует по Bluetooth Classic с находящимся рядом другим смартфоном с такой же версией приложения OpenDigitalCash. Прием и отправка выглядят одинаково на обоих устройствах.

ODC_p2p_demo.compressed.mp4

Цель

Показать принципиальную возможность реализации цифрового аналога фиатной валюты (см. Электронные Деньги):

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

Особенности реализации

Multi-module, Clean Architecture, SOLID, DI, MVI, MVVM, Unit-Testing (Junit5), Material Design, Material 3.

  • Многомодульный проект (20+ модулей: app, 14 core, 6 feature), с отдельным модулем included build (build-logic), используются разные buildTypes (debug / release)
  • Написаны собственные Gradle плагины (Convention Plugins) для конфигурации сборки Gradle (вынесена общая конфигурация и зависимости для модулей для избегания дублирования кода, удобства изменения и расширения сборки)
  • Использованы каталоги версий (Version Catalogs).
  • Между модулями четко определены границы и область применения, например
    • Есть чистый Kotlin-модуль :core:model , где хранятся модели (вроде UserInfo), который сам не зависит от других, но от которого зависят другие модули, в том числе UI
      • Другие модули, например :core:database (Room), имеют аналогичные внутренние модели с дополнительными свойствами (например, @Entity) и конвертеры из общих моделей :core:model в локальные
      • На границе модулей происходит преобразование между внутренними и общими моделями

Примечание: скорее всего, возможно ещё улучшить независимость модулей, если найти способ преобразовать модели в :core:wallet в локальные и вынести аналогичные общие в модуль :core:model. Таким образом будет убраны лишние связи других модулей с :core:wallet, которые возникают только из-за общих моделей.

Модули проекта

Все модули

App модуль

:app: :build-logic: :core: :feature:
convention common-android atm
common-jvm history
connectivity home
database p2p
datastore settings
design-system wallet-details
domain
model
network
testing
transaction-logic
ui
wallet
wallet-repository

Note

В этой версии модуль, реализующий кошелек, является открытым, а банкноты хранятся в базе данных. В Минимально Жизнеспособном Продукте кошелек должен находиться в отдельном защищенном разделе смартфона (например, на специальной симкарте OpenDigitalCash; потеря симкарты равна потере кошелька).

  • Использованы Kotlin Coroutines и Flows (с соблюдением структурированного паралеллизма) для асинхронного выполнения и возможности отмены всех ресурсоемких операций приложения.

  • Для UI использован Jetpack Compose со стандартной библиотекой навигации и 2 NavHost (вложенная навигация), паттерн MVI. Всего 7 основных экранов, из которых 3 верхнего уровня.

  • UI работает в режиме edgeToEdge() , адаптируясь и к системной навигации 3-мя клавишами и жестами, и к открытию IME (экранной клавиатуры). Состояние приложения полностью сохраняется и адаптируется при смене конфигурации смартфона.

  • Используются кастомные анимации.

Протоколы транзакций

Оффлайн передача банкнот между кошельками (P2P)

Схема нового алгоритма передачи (схему старого можно найти в документации ODC к прошлой версии приложения):

ODC_wallet_to_wallet_diagram (dark)

Протоколы передачи

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

Протокол 1

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

  • Получатель ожидает лидирующий массив байт с размером, далее последовательно собирает большой пакет из фрагментов, после чего большой пакет (массив байт) обрабатывается по протоколу 2.

Протокол 2:

  • Для двойной сериализации и десериализации многоуровневых DTO в массивы байт и обратно:

    • сначала сериализации DTO в массив байт
    • далее обёртка этого массива в другой DTO (DataPacket) вместе с одним из заранее определенных типов, который соответствует этому пакету
    • повторная сериализация в массив байт
    • и отправка получателю по протоколу 1
  • Получателю известно, что пришедший массив байт всегда является сериализованным DataPacket, поэтому после получения по протоколу 1 всего пакета, он

    • десериализует массив байт в DataPacket DTO,
    • откуда извлекает тип передаваемого вложенного DTO
    • и, в зависимости от типа, десериализует вложенный массив байт в DTO, который уже используется для транзакций.

Оба этих подхода в совокупности позволяют легко расширять DTO и добавлять их новые типы независимо от алгоритма передачи данных, при этом протокол 1 и протокол 2 никак не зависят друг от друга.

Для обоих протоколов написаны Unit-тесты (Junit5).

Поиск пользователей приложения по Bluetooth

Обычный Bluetooth не позволяет отфильтровать пользователей по UUID сервиса, поэтому реализована фильтрация по имени устройства.

Для отправителя

Начинается поиск устройств Bluetooth, но фильтруются и остаются только те, у которых в начале имени есть определенный префикс.

Для получателя

  1. Перед началом вещания оригинальное имя устройства Bluetooth сохраняется в Android Datastore (для того, чтобы откатить его обратно, в том числе даже при падении и восстановлении работы приложения)
  2. Далее имя пользователя, указанное в разделе настроек ODC, загружается из DataStore, к нему добавляется префикс
  3. Bluetooth-имя устройства Android меняется на новое.
  4. После отмены или успешного подключения Bluetooth-имя устройства Android сразу же меняется на старое, сохраненное ранее в DataStore.

Таким образом, личные настройки смартфона пользователя сохраняются нетронутыми.

Выбор банкнот из кошелька по заданной сумме

Пользователь запрашивает сумму из своего кошелька; эта же ситуация может возникнуть и с обычным банкоматом.

Задача: можно ли из имеющихся банкнот (неделимых, как и бумажных) собрать точную сумму, и если да, то из каких именно?

Является задачей о сумме подмножеств, NP-Complete или NP-hard (в зависимости от задачи). Решена здесь при помощи динамического программирования с использованием Kotlin Coroutines, написаны Unit-тесты на Junit5.


КИБ, OpenDigitalCash

About

Open Digital Cash. Android application.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Kotlin 100.0%