Это усеченный по порядку (алгоритму), заданному в программном обеспечении ERACHAIN, открытый ключ. На счетах хранятся принадлежащие пользователю учетные единицы. К счетам привязаны транзакции в блокчейне.
Установка временных ограничений на соединение с пиром, в действиях которого выявлено отклонение от протокола.
Набор прошедших валидацию транзакций, снабженный заголовочными атрибутами, в числе которых обязательно есть ссылка на блок-предшественник.
Порядковый номер блока в блокчейне.
Подпись предыдущего блока.
Цепочка блоков — набор валидных блоков.
Передача сообщений всем участникам сети.
Последний блок цепи, ниже которого изменения цепочки не могут происходить.
Это Пользователь, обладающий количеством Расчетных единиц ERA в размере не менее 100 (ста), который может собирать наборы транзакций и формировать из них Блок.
Процесс формирования нового блока. При выигрыше, принятии сетью нового блока, происходит начисление комиссии на адрес, сформировавший блок, в размере суммы комиссий со всех транзакций в блоке, но не менее 0.00032768 COMPU.
Cоздание временной копии рабочей базы данных.
Блок, который поставляется вместе с программным обеспечением Erachain, имеющий электронную цифровую подпись в формате Base58: V5RkNcBrDi88Tbkm71DN1Po7M7yEop9kpu95gxHjPvR7MDa3uMkYotvGJ8awRTmz2abBEGXShZptrVdzmwuMsag и являющийся первым блоком цепочки.
Функция, производящая необратимое преобразование произвольной структуры данных в единственное число фиксированной длины.
Любой компьютер, использующий программное обеспечение Erachain и соблюдающий правила протокола.
Узел, на котором запущена платформа Erachain, с которой установлено соединение и идет обмен данными.
Отправка запроса на доступность узла.
Транзакция в виде байт массива в кодировке Base58. Структура формирования байт массива транзакции зависит от типа транзакции. Формат транзакции представлен в Приложениях А.
Mесто, где хранятся и поддерживаются исходные файлы приложения.
Интерфейс для доступа к приватным данным.
Программный интерфейс для обеспечения обмена данными между нодами.
Величина актива ERA, на счету форжера, скорректированная коэффициентами уменьшения для сумм недавно поступивших на счет.
Обособленные данные, подписанные цифровой подписью, включаемые в блоки.
Транзакции полученные от пиров, ожидающие включения в блок.
Отражение баланса имущества - свободный остаток актива на счету и входящий оборот.
Тоже, но для баланса займов (аренды недвижимого имущества).
Тоже, но для баланса хранения (обычно у движимого имущества).
Тоже, но для баланса потребления.
Тоже, но для баланса потребления.
Cекретный набор символов, используемый для вычисления на его основе пары ключей (открытый и закрытый).
Отдельная цепочка блоков, обрабатываемая ПО Erachain и имеющая свой генесиз блок. Сеть такой цепочки работает на портах 905Х.
Это внутренняя учетная единица блокчейн платформы Erachain, дающая право пользователю создавать блоки, получая за это Расчетные единицы COMPU.
Это внутренняя учетная единица блокчейн платформы Erachain, использующаяся для совершения транзакций.
Платформа Erachain - это распределенный реестр для хранения данных о транзакциях, их верификации на основании алгоритма консенсуса. Реестр формируется на основании данных о транзакциях, группируемых в блоки. В целях предотвращения изменений, блоки формируются в цепочку блоков.
Erachain имеет структуру и алгоритм работы классической блокчейн среды, имеющей генезис-блок, задающий начальное состояние системы, базу логирования транзакций и базу текущего состояния, где хранятся текущие балансы на счетах и иная информация.
Отличительной чертой платформы является удобная персонификация членов платформы. Необходимость раскрывать личные данные преследует нас постоянно. Классический подход к хранению и обработке персональных данных переживает настоящий кризис, особенно в последние годы, когда на фоне непрекращающегося роста объемов цифровой информации появились многочисленные случаи хакерских атак, целью которых является подмена и искажение информации. Технология блокчейн может стать решением данной проблемы.
Но текущая реальность такова, что полноценное использование блокчейн проектов банками и другим традиционными институтами невозможно ввиду отсутствия механизма для соблюдения правил AML/KYC.
В основе технологии платформы Erachain лежит механизм удостоверения личности. Данные новых пользователей заносят и верифицируют другие идентифицированные участники экосистемы, которые выступают гарантами подлинности этих данных. Таким образом, платформа позволяет использовать ее в качестве инструмента KYC для взаимодействия в бизнес-процессах.
Графический интерфейс платформы предоставляет широкие возможности для автоматизации рабочих процессов. Готовые формы с интуитивно понятным интерфейсом не требуют дополнительного программирования.
Работа среды Erachain базируется на двух учетных единицах - ERA и COMPU. Единица ERA дает право собирать блоки и подтверждать данные пользователей в сети, а единица COMPU используется для оплаты транзакций и в качестве вознаграждения за сборку блоков. Такая реализация протокола позволила ввести независимые друг от друга правила эмиссии токенов и повысить экономическую эффективность платформы.
В отличие от других блокчейн платформ, в которых скорость генерации блоков нестабильна и может составлять от нескольких секунд до нескольких часов, платформа Erachain имеет стабильную скорость формирования блоков, которая составляет всего 30 секунд.
В платформе используются следующие алгоритмы хеширования:
При первоначальной установке программного обеспечения ноды происходит генерация сида кошелька. На основании этого сида может быть сгенерировано любое количество сидов счетов. В веб версии кошелька возможна генерация мнемонической фразы в соответствии с BIP39 и выработка на ее основании сида кошелька.
Счет представляет собой буквенно-цифровую строку в кодировке Base58, полученную из открытого ключа пользователя с использованием хэш-функции, дополненную контрольными значениями (используемые для обнаружения ошибок). Адрес счета используется для отправки и получения цифровых активов в качестве идентификатора получателя и отправителя, а также в других транзакциях в соответствии с Приложениями А-Л.
Массив из 32 элементов заполняется случайными значениями Для генерации случайных значений используется стандартная библиотека Java SecretRandom.
Для разных вариантов цепочек, вычисляем значение подписи по алгоритму Ed25519.
Транзакция представляет собой массив байтов, сериализованный согласно своему типу, описанному в схеме Приложение А-Л. Полученный результат дополняется реквизитом подписи транзакции, затем преобразуется в строку по формату Base58.
Транзакции обеспечивают отражение в реестре всех операций в среде, например таких, как выпуск и передача между сторонами активов. Аналогом такой операции может являться запись по счету клиента в банке. Полный перечень видов транзакций представлен в Приложениях А.
Короткая ссылка на транзакцию - это есть 8 байт, где первые четыре байта - это номер блока и следующие четыре байта - это номер транзакции в нем. Такая ссылка именуется далее SeqNo.
Блок представляет собой специальную структуру для записи группы транзакций, сформированных согласно описанным в настоящем документе алгоритмам. Обязательным параметром заголовка блока является ссылка на предыдущий блок.
Форжер, обладающий генерирующим балансом превышающем 100 ERA, может сформировать блок-кандидат на включение в цепочку и разослать его своим пирам.
Атрибут reference - подпись блока-предшественника (связь с предыдущим блоком является гарантией от возможного внесения изменений прошлыми периодами, а также основа самого принципа blockchain), имеющего предшествующий номер высоты.
Атрибут transactions - массив, состоящий из raw transactions.
Атрибут transactions hash - значение хеш-функции SHA256 от конкатенации байтовых массивов подписей транзакций.
Атрибут block signature - подписанный по алгоритму SHA512 ключом форджера набор данных блока.
Подробное описание структуры блока и структуры подписи приведено в Приложении Н.
У каждого типа транзакции своя индивидуальная процедура валидации, например, у транзакций по переводу актива проводится проверка достаточности баланса отправляемого актива на счете создателя транзакции.
Проверка подписи транзакции осуществляется при получении данной транзакции из сети.
Проверки по базовому классу транзакции:
Все ноды одновременно конкурируют, чтобы получить право записать следующий блок. Каждый пользователь действует исходя из желания получить наибольшую личную финансовую выгоду, поэтому встает вопрос, а почему пользователь должен распространять блок, созданный другим пользователем?
Второй важный вопрос, а кто разрешает конфликты, когда несколько нод принимают валидным блок примерно в одно и то же время? Чтобы выполнить эту работу, блокчейн использует модель консенсуса, которая позволяет группе взаимно недоверчивых пользователей работать вместе.
Когда новый пользователь присоединяется к блочной системе, он соглашается с исходным состоянием системы. Такое состояние определяется генезис-блоком и всей цепочкой блоков, последовательно присоединенных к нему. Каждый блок формируется из набора транзакций, признанных валидными. В протоколе блокчейн жестко задан блок генезиса, который является первым в цепочке и каждый последующий блок должен быть добавлен к блочной цепочке после него на основе консенсуса. Каждый блок должен быть действительным и, следовательно, может быть независимо проверен каждым пользователем сети на валидность.
Для достижения консенсуса должны выполняются следующие правила:
По мере создания новых транзакций происходит их рассылка пирам. Каждая нода ведет перечень таких неподтвержденных транзакций. Ноды, осуществляющие форжинг новых блоков, формируют блоки с такими транзакциями и рассылают их своим пирам. Нода, при получении нового блока, проводит его валидацию (п. 1.4.2.) и оценивает допустимость включения данного блока в цепочку. Форжинг возможен при условии наличия на счету суммы не менее 100 ERA. Блок, включенный в блокчейн, начисляет комиссии с транзакций в сумме не менее 0.00032768 COMPU, содержащихся в нем, на счет создателя блока.
Консенсус по методу Proof of Stake основан на идее, что чем больше активов есть у пользователя в системе, тем более вероятно, что он заинтересован в успешности и развитии системы, и менее вероятно, что он будет предпринимать действия, которые принесут вред системе. Метод Proof-of-Stake использует размер доли, которую пользователь имеет в системе в качестве одного из определяющих факторов для создания нового блока. Такие пользователи с большей вероятностью получат возможность создавать новые блоки.
Платформа Erachain позволяет передать право на выпуск нового блока другому пользователю сети через предоставление актива ERA в заем. Достижение консенсуса по блоку-кандидату осуществляется на базе принципа голосования.
В классе Controller, метод onMessage, происходит обработка потока транзакций и новых блоков, поступающих от пиров.
По получении нового блока запускается процедура проверки блока.
В случае если получен блок, у которого reference соответствует подписи последнего блока в рабочей цепочки блоков, запускается процедура валидации.
Если у поступившего блока и последнего блока в цепочке одинаковый reference, то есть они конкурирующие, производится вычисление функции победителя. Если значение данной функции у поступившего блока строго больше значения функции стоящего в цепочке, производится замена блока в цепочке.
Периодичность генерации блоков жестко задана протоколом и составляет 30 секунд до блока ХХХ и 30 секунд после него.
Определение величины баланса актива на счете.
forgingValue = creator.getBalanceUSE(Transaction.RIGHTS_KEY, dcSet).intValue()
Определение победного значения (Выигрыш, Strength of account).
winValue = BlockChain.calcWinValue(dcSet, this.creator, this.heightBlock, this.forgingValue)
Для проверки валидности блока по его силе необходимо вычислить:
Текущая цель
Значение текущей цели currentTarget является значением цели из последнего собранного блока (это среднее значение выигрыша за последние 1024 блока)
currentTarget = this.parentBlockHead.target
Приведенное победное значение для текущей цели
targetedWinValue = BlockChain.calcWinValueTargetedBase(dcSet, this.heightBlock, this.winValue, currentTarget)
Если значение получается отрицательное (в результате переполнения) или больше чем 10 базовых целей BASE_TARGET, то результирующее значение приведенного выигрыша targetedWinValue приравнивают к 10 базовым целям.
Новая цель target для текущего блока
Для текущего блока вычисляется его новая цель target как средняя величина победных значений за последние 1024 блока
target = BlockChain.calcTarget(this.heightBlock, currentTarget, this.winValue)
Если высота меньше TARGET_COUNT расчет цели производится по упрощенной формуле:
target = currentTarget - currentTarget/heightBlock + winValue/currentTarget
Значение цели ограничивается сверху величиной 1,5 * currentTarget
Новая цель определяется по следующей формуле:
target = (1023*currentTarget + winValue)*1024
Синхронизация блоков контролируется главным потоком приложения, описанном в классе BlockGenerator. Поток представляет собой бесконечный цикл, стартует при запуске приложения и завершается при выходе из приложения. Он отслеживает состояние цепочки блоков, при ее отставании от актуальных значений, запрашивает у пиров необходимые блоки, обрабатывает события принудительного отката блоков на заданное число блоков и полный сброс цепочки до генезис блока. Детальная реализация данного механизма отражена в классе Synchronize.
Для синхронизации выбирается пир, имеющий максимальную высоту цепочки. Если высота цепочек совпадает, то выбирается цепочка с наибольшим весом. Весом цепочки является сумма победных значений (WinValue) блоков.
Далее делается запрос к пиру выдать все номера блоков с высотой, превышающей текущее локальное значение. По получении ответа запускается процесс запроса блоков по полученным сигнатурам. Поток работает в последовательном режиме.
Каждый полученный блок, в рамках процесса синхронизации, проверяется на валидность, и добавляется в копию базы данных через механизм транзакций. Формируется набор блоков для добавления в основную базу. Когда проверка новой цепочки, полученной с этого пира, завершена и правила протокола соблюдены, то есть все блоки в ней признаны валидными, а заявленная пиром высота и вес цепочки подтвердились, происходит добавление сформированного набора новых блоков в основную базу данных.
Если в ответ на запрос нового блока поступает сообщение об отсутствии такового, тогда происходит поиск общего блока и запрос блоков начинается с последнего общего блока. В этом случае, формируется очередь для обработки блоков, которые валидируются в отдельном потоке и группируются в цепочку.
Для предотвращения глубокого отката и вредоносного воздействия, направленного на изменение истории транзакций, существует понятие чекпоинт, ниже которого откатывать цепочку нельзя. Если при поиске последнего блока достигнут чекпоинт, значит цепочка невалидна и синхронизация с ней невозможна, а пир банится.
Платформа Erachain построена на одноранговой сети (Peer to Peer) или децентрализованной сети, не имеющая критических или руководящих точек. Все узлы (nodes), которые участвуют в сети, равны.
Взаимодействие между нодами осуществляется через сокет. Сокет представляет собой одно соединение между двумя сетевыми приложениями. Нода открывает сокет для каждого пира. Такое соединение является двунаправленным, т.е. обе стороны соединения способны отправлять и получать данные. Каждое сообщение рассылается через сокет всем подключённым пирам.
Платформа может работать в двух режимах “для разработчиков” и “эксплуатации”.
В режиме “для разработчиков” для обмена сообщениями, нодами используется порт 9065, в режиме “эксплуатации” используетися порт 9045.
В платформе представлены следующие 14 типов межсетевых сообщений:
Ноды между собой взаимодействуют посредством передачи сериализованных сообщений согласно соответствующему типу сообщений в схеме, по принципу схожему с сериализацией транзакций (пункт 1.3): примитивные типы данных преобразуются в массив байтов и отправляются через контроллер работы с сетью.
Изначально программе известны только пиры, которые находятся в дистрибутиве (файл peers.json). Для получение новых пиров производится запрос к работающему пиру из известных пиров. Полученные новые пиры проверяются (пингом), информация о них сохраняется в базе данных, затем устанавливается соединение с ними.
Числа BigDecimal передаются сжатыми до 8 байт, что не позволяет передавать числа размером более 23-х разрядов. Поэтому число разрядов после десятичной точки передается обычно отдельно как целое число от 0 до 32, способ вычисления которого приведен в приложении О.
С версии 5.0 ПО Erachain позволяет запускать отдельные цепочки блоков, которые не связаны с основной цепочкой (Main Net).
В таких “цепочках на стороне” подпись создается не добавлением порта, а добавлением подписи генезис-блока, так как она у каждой цепочки получается уникальной.
Вдобавок первые 4 байта каждого сообщения (magic mask) так же получают значение 4-х первых байт подписи блока и в случае несовпадения внешняя нода блокируется отсеивая ноды с другими цепочками.
MainNet - основная сеть, основная цепочка данных. Порты 904Х. Данные на подпись формируется путем добавления порта к байтовому массиву данных.
TestNet - тестовые сети и цепочки. Порт сети - 906Х. Данные на подпись формируются как и для MainNet. Служит для быстрой проверки и тестирования.
DemoNet - демонстрационная сеть и цепочка. Порт сети - 906Х. Данные на подпись формируются как и для MainNet. Цепочка задается разработчиками и сбрасывается периодически на новую цепочку. Служит для быстрой демонстрации.
SideNet - сторонние сети и цепочки - сайдчейны (sidechain). Порт сети -905Х. Данные на подпись формируются путем сложения данных и подписи генезис-блока.
Для интеграции внешних интерфейсов введена команда API выдающая подпись генезис-блока сайдчейна, которую надо добавлять к подписи транзакций: api/info. Ответ содержит параметр side, в котором указа подпись генезис-блока, его время создания и название цепочки.
Баланс - это совокупность двух значений его сторон - всего получено (№1) и остаток (№2). Разница между ними - сколько всего выдано.
Для учета разносторонней бухгалтерской деятельности и вообще учета чего-либо, создана сисима балансов так что на каждом счете есть 5 позиций балансов (position) и в каждой позиции по 3 стороны (side) баланса.
Позиции:
1. Имею (OWN) - Имущественный баланс. Ведет учет владения. Создатель транзакции списывает со своего счета и начисляет на счет получателя средства - передает средства (прямая транзакция). Этот баланс как правило допускает только прямые транзакции, - то есть принудительный возврат (снятие с получателя - возвратная транзакция) запрещен. Возможно уходить в минус если актив выпущен без ограничения количества. Тогда общий минус на счете создателя отражает сколько он начислил этого актива на другие счета.
2. Долг (DEBT) - Долговой баланс. Ведет учет долгов. Создатель может как передать на счет получателя, так и забрать со счета получателя (возврат). Однако у разных типов активов эти направления действий могут быть запрещены. Минус у остатка означает сколько выдано займов, плюс - сколько взято в долг. Дополнительно ведется раздельный учет (в отдельной таблице) сумм балансов по парам счетов - заемщик и должник, так чтобы можно было сразу понять какая сумма долга между двумя участниками. По этой сумме проходит проверка для каждой транзакции на предмет что нельзя вернуть долга больше чем остаток по заданной паре заемщик - кредитор. Как правило нельзя дать в долг сумму большую чем у тебя в имуществе и уже выдано займов.
3. Храню (HOLD) - Складской баланс. Ведет учет у кого на руках сколько хранится. Тут всегда транзакция обратная (возврат) - то есть создатель транзакции всегда начисляет на свой счет и списывает со счета "получателя".Суть транзакции: создателем берутся на себя обязательства того, что он принял на ответственное хранение у кого-то, что-то, в каком-то объеме. Обычно используется для учета хранения, доставки, охраны, лизинга и т.д., без перехода права собственности. Отрицательный остаток может быть только у создателя безграничного актива.
4. Трата (SPEND) - Потребительский баланс. Отражает учет сколько было потреблено или произведено актива. Получателем может быть только создатель транзакции и поэтому получатель не указывается. Потребление (прямая транзакция) актива списывает актив с имущественного баланса и начисляет па потребительский баланс. Создание (обратная транзакция) актива начисляет актив на имущественный баланс и списывает с потребительского баланса. Хождение между разными счетами запрещено. Этот баланс может использоваться так же как учет погашенных долгов векселей и т.д.
5. Залог (PLEDGE) - Залоговый баланс. Ведет учет количества заложенных активов. Например при выставлении заявки на биржу, количество актива переносится с баланса №1 (Имею) на №5 (Залог). Так что всегда можно понять сколько по данному счету актива в ордерах на бирже заложено.
Стороны у баланса:
№ 1. Всего пришло
№ 2. Остаток
№ 3. Всего ушло (вычисляется как: 1-я сторона минус 2-я сторона.Исключение - для актива COMPU - У него 4-я позиция, на стороне 2 - это всего сколько потратил на комиссии. А в позиции 5, на стороне 2 - всего сколько нафоржил).
MAINNET - основня цепочка Erachain.
Порты работы:
Подпись транзакций формируется путем сложения байт-кода транзакции и 4-х байтов от значения порта, как показано ниже в приложениях.
TESTNET - цепочка для тестов. Порты 906Х. Запускается путем указания ключа при запуске -tesnet=[TIMESTAMP], где TIMESTAMP - время создания генезис-блока, в секундах. При этом можно запустить несколько нод в одной тестовой цепочке если указать одинковое время старта TIMESTAMP. Так же можно запустить цепочку на всего одной ноде если указать простой параметр старта -tesnet - в этом случае нода не запускает сетевой модуль и считает что она всегда в синхронизации, а скорость блоков при этом равна 5 секундам.
В тестовой цепочке на каждом новом счете содержится по 100 COMPU и некоторое (сгенерированное рандомно) число ERA, достаточное для генерации блоков. Вдобавок неудостовренный счет может вносить и удостоверять персон.
Подпись транзакций формируется путем сложения байт-кода транзакции и 4-х байтов от значения порта.
DEMONET - цепочка для демонстраций возможностей. Запускается путем указания ключа при запуске -tesnet=demo - это такая же тестовая цепочка (порты теже), но с предустановленным временем генезис-блока. В этом случае нода подключится к другим узлам, поддерживающим данную цепочку. Такой режим полезен если нужно показать ранее сохраненные данные в ДЕМО-цепочке.
SIDECHAIN - цепочка пользовательская, бесплатная, со сформированным пользователем генесиз-блоком и установленными параметрами протокола. Для этого используются файлы sideGENESIS.json, sidePROTOCOL.json, sidePEERS.json. Более подробно о запуске сайдчейна см. в папке z_SIDECHAIN_EXAMPLES приложения. Порты 905Х.
Подпись транзакций формируется путем сложения байт-кода транзакции и байтов подписи генсиз-блока, см API-команду info.
CLONECHAIN - цепочка брендированная пользовательская (Whitelabel), со сформированным пользователем генезис-блоком и установленными параметрами протокола. Для этого используются файлы chainGENESIS.json, chainPROTOCOL.json, chainPEERS.json. Порты любые кроме 904Х-906Х.
Подпись транзакций формируется путем сложения байт-кода транзакции и байтов подписи генсиз-блока, см API-команду info.
Выводы:
Таким образом можно всегда сказать что если порты 904Х - это это майннет, если порты 905Х - это бесплатный сайдчейн, если 906Х - это тестовая (или демо) и все остальные - это брендированный блокчейн (клончейн).
Приведение типов производится к типам Java
А.0. Заголовок Транзакции TX_HEAD
п/п | Наименование | Формат | Приводим к виду | Значение по умолчанию |
---|---|---|---|---|
1 | HEAD (+) | byte[4] | [TYPE, VERSION, FLAGS1, FLAGS2] | |
2 | TIMESTAMP | byte[8] | long | |
3 | extFLAGS (+) | byte[8] | long | 0 (расширенные Флаги, пока не используются) |
4 | CREATOR PUBLIC KEY (+) | byte[32] | byte[32] | |
5 | EX_LINK (+) | byte[] | ExLink | if FLAGS1 & 32 |
6 | FEE POW | byte[1] | byte[1] | [0] |
7 | SIGNATURE (+) | byte[64] | byte[64] |
Создание байт-кода для подписи
Если сборка байт-кода осуществляется для целей набора транзакций в пакет, то используются только поля помеченные (+).
Для создания байт-кода для подписания, поле SIGNATURE не используют.
Для создания уникальности подписи между MainNet, TestNet (DemoNet), Sidechain и Clonechain используют добавку к байт-коду перед созданием подписи:
Вид сети | Что добавляем | Формат | Примечание |
---|---|---|---|
MainNet, DemoNet | PORT | byte[4] | |
Sidechain, Clonechain | GENESIS_SIGNATURE | byte[64] | см. сайдчейны |
Сущность - это Актив, Персона, Статус и т.д. - все что имеет порядковый номер и создается из выпускающих транзакций.
п/п | Наименование | Номер | Примечание |
---|---|---|---|
1 | ASSET | 1 | Активы |
2 | IMPRINT | 2 | Хэши |
3 | TEMPLATE | 3 | Шаблоны |
4 | PERSON | 4 | Личности |
5 | STATUS | 5 | Назначение |
6 | UNION | 6 | Объединение |
7 | STATEMENT | 7 | Заявление (Нота) |
8 | POLL | 8 | Выбор (Голосования) |
У всех сущностей есть общий набор полей - заголовок
п/п | Наименование | Формат | Приводим к виду | Значение по умолчанию |
---|---|---|---|---|
1 | TYPE | byte[2] | int | [SUB_CLASS,0] |
2 | MAKER | byte[32] | byte[32] | |
3 | NAME LENGTH | byte[1] | uint | |
4 | NAME (+) | byte[NAME LENGTH] | String UTF-8 | max 256B |
5 | ICON LENGTH | byte[2] | uint | |
6 | ICON | byte[ICON LENGTH] | variable | max 64kB |
7 | IMAGE LENGTH | byte[4] | uint | знаковый и 7-й биты используются как флаги |
8 | IMAGE (+) | byte[IMAGE LENGTH] | variable | max 1GB |
9 | APP_DATA | byte[] | variable | |
10 | DESCRIPTION LENGTH | byte[4] | uint | |
11 | DESCRIPTION (+) | byte[DESCRIPTION LENGTH | String | max 2GB |
12 | REFERENCE | byte[64] | byte[64] | не используется при создании актива |
13 | DB_REF | byte[8] | long | SeqNo |
REFERENCE и DB_REF не используются при создании актива, но заполняются после при внесении транзакции в базу - так как номер транзакции становится известен.
(+) - означает что данное поле используется для создания подписи под данными Сущности, см A.24.0.
Если поднят флаг в знаковом бите IMAGE_LENGTH значит в данных так же присутствуют расширяющие данные - APP_DATA. Надо этот флаг снять чтобы не исказить значение длинны картинки.
п/п | Наименование | Формат | Приводим к виду | Значение по умолчанию |
---|---|---|---|---|
1 | LENGTH | byte[4] | int | |
2 | DATA: | byte[] | ||
2.1 | FLAGS1 | byte[2] | 0 | |
2.2 | FLAGS2 | byte[8] | long | см А.А.3 |
START_DATE | byte[8] | long | if set START_FLAG | |
STOP_DATE | byte[8] | long | if set STOP_FLAG | |
TAGS_LEN | byte | byte | if set TAGS_FLAG | |
TAGS_STR | byte[] | String | if set TAGS_FLAG | |
2.3 | ICON_TYPE = DATA[10] | byte | знаковый бит поднят, значит Иконка по ссылке (URL_FLAG). Тип медиа см. в А.А.3 | |
2.4 | IMAGE_TYPE = DATA[11] | byte | знаковый бит поднят, значит Картинка по ссылке (URL_FLAG). Тип медиа см. в А.А.3 |
Флаги ссылок URL_FLAG: если иконка/картинка не загружается, а вместо данных используется внешняя ссылка, то в данные записывается байткод строки URL в UTF-8.
Наименование | Значение | Примечания |
---|---|---|
START_FLAG | [0] = 64 | задано время начала |
STOP_FLAG | [0] = 32 | задано время окончания |
TAGS_FLAG | 1L << 63 | флаг меток |
Если поднят флаг начала или окончания, то следующие 8 байт это таймстамп для данного значения в миллисекундах.
Если поднят TAGS_FLAG значит пользоватль задал строчку меток (tags) - это слова или словосочетания, разделенные запятыми, по которым можно делать быстрый поиск в блокчейн. Строка меток преобразуется в байты длинною не более 255 байт и вносится TAGS_STR, а длинна байт задается в TAGS_LEN (см. А.А.2)
MEDIA_TYPE | Значение | Примечания |
---|---|---|
IMAGE | 0 | .gif, jpg, .png |
VIDEO | 1 | .mp4 |
SOUND | 2 | .mp3 |
FRAME | 10 (reserved) |
Пример сборки APP_DATA:
Допустим пользователь загрузил иконку как видео-файл, а для отображениия картинки выбрал внешнюю ссылку на картинку, тогда получаем:
DATA[0] = -128
DATA[10] = ICON_TYPE = 1
DATA[11] = -128 | IMAGE_TYPE = -128.
Так же если особые значения не используются, тоесть и иконка, и картинка это обычные файлы картинок, то APP_DATA нет смысла собирать и вставлять в байткод сущности. Соответственно все работает по старому.
public static byte[] makeAppData(long flags, boolean iconAsURL, int iconType, boolean imageAsURL, int imageType) {
if (flags != 0 || iconAsURL || imageAsURL || iconType != 0 || imageType != 0) {
.
п/п | Наименование | Формат | Приводим к виду | Значение по умолчанию |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[21,0,0,0] | |
7 | ASSET ITEM | byte[] | byte[] | Собирается из полей Актива, см. таблицу ниже |
п/п | Наименование | Формат | Приводим к виду | Значение по умолчанию |
---|---|---|---|---|
1 | HEAD_DATA | byte[] | см. А.А.0 | |
12 | QUANTITY | byte[8] | long | - только если субкласс VENTURE |
13 | SCALE | byte[1] | int | - только если субкласс VENTURE |
14 | ASSET_TYPE | byte[1] | int | см. таблицу ниже |
п/п | Наименование | Номер | Описание |
---|---|---|---|
1 | UNIQUE | 1 | Уникальный - без количества и точности. Не имеет полей Количество и Точность |
2 | VENTURE | 2 | Обычный с количеством и точностью |
**Так же для целей удобства Уникальный класс нужно использовать если пользователь задал количество для выпусска = 1. Тогда активу назнаяаем 1 и не используем Количество и Дробность
Полный список типов Активов с описанием можно получить через API вызов:
п/п | Наименование | Номер | ASSET SUB CLASS | Описание |
---|---|---|---|---|
1 | Движимое имущество | 0 | Все вещи который можно двигать | |
2 | Обычный Актив (Токен) | 1 | 2 | Как правило то что во внешнем мире существует |
3 | Недвижимое | 2 | 2 | Дома, земля и т.д. |
4 | Внешняя валюта | 11 | 2 | Деньги во вне |
5 | Внешний сервис | 12 | 2 | Услуга во вне |
6 | Внешние акции, доли | 13 | 2 | Акции предприятий |
7 | Вексель | 14 | 1 | Внешний вексель цифровой |
8 | Вексель Экс | 15 | 1 | Переводной вексель |
9 | Мой долг | 26 | 2 | Мой долг перед другим лицом - это некоторое обязательство |
10 | Человеко-час | 34 | 2 | Час рабочего времени человека |
11 | Человеко-минута | 35 | 2 | Минута рабочего времени человека |
12 | Внешние права требований | 49 | 2 | Внешние права требований на все что угодно |
13 | Цифровые деньги | 51 | 2 | Внутренние активы в виде денег |
14 | Цифровые услуги | 52 | 2 | Внутри блокчейна услуги |
15 | Цифровые акции, доли | 53 | 2 | Внутренние - нет внешнего учета - только тут учет |
16 | Цифровые Бонусы | 54 | 2 | Бонусы можно переводить с удостоверенного на удостоверенные счета |
17 | Внутренние права Доступа | 55 | 2 | Для управления правами на внутренние услуги |
18 | Цифровой голос | 56 | 2 | Единица голоса |
19 | Банковская гарантия | 60 | 1 | Одна гарантия |
20 | Сумма банковских гарантий | 61 | 2 | Общее число денег по всем выданным банковским гарантиям |
21 | NFT - невзаимозаменяемый токен | 65 | 1 | NFT |
22 | Цифровой индекс | 100 | 2 | Индекс на внешние активы, нет ограничения по переводу между неудоствренными счетами |
23 | Цифровое право требования | 119 | 2 | Другие права требований на внутренние активы |
24 | Счетные единицы | 123 | 2 | Не имеют стоимости или требований, обмениваются на бирже только на другие учетные единицы. Служит для внутреннего учета и отчетности. Они ничего не стоят, можно делать любые действия от своего имени |
25 | Собственный учетный актив | 124 | 2 | Только я сам могу им распоряжаться |
26 | Мой учетный долг | 125 | 2 | То что я должен другим людям. Назначается самим пользователем для личного учета |
27 | Учетный фонд помощи (касса взаимопомощи) | 126 | 2 | Фонд взаимопомощи - учетный, для бухгалтерского учета |
28 | Собственный касса денежная | 127 | 2 | Например учет кассы ТСЖ. c требованиями к оплате и с автоматическим снятием требования (DEBT) при погашении |
29 | Собственный учет имущества | 128 | 2 | Имущество с прямым начислением на ИМЕЮ (учет). Например учет имущества в ТСЖ. доступны 4 баланса и у каждого работает Возврат - backward |
30 | Собственный учет долей | 129 | 2 | Доли с прямым начислением на ИМЕЮ. Например учет долей в ТСЖ. доступны 4 баланса и у каждого работает Возврат - backward |
Если актив имеет список выплат (роялти) при сделках на бирже с этим активом, то поднимаем флаг в младшем бите FLAGS2. При этом сам список содержит первый байт - количество в (списке - 1) (таким образом помещается максимум 256 значений) , а далее сами значения объектов вида «Счет» ExLink (см. А.35.3.2)
Дополнительные Флаги Активов в FLAGS2
п/п | Наименование | Значение | Описание |
---|---|---|---|
1 | Список Роялти | 1 (0й бит) | В данных есть список роялти |
2 | Непередаваемый | 2 (1й бит) | Значит актив нельзя передать |
3 | Анонимное владение запрещено | 4 (2й бит) | Нельзя передать или продать анониму |
п/п | Наименование | Формат | Приводим к виду | Значение по умолчанию |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[22,0,0,0] | |
6 | IMPRINT ITEM | byte[] | byte[] | см. таблицу ниже |
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | HEAD_DATA | byte[] | см. А.А.0 |
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[23,0,0,0] | |
7 | TEMPLATE ITEM | byte[] | byte[] | см. таблицу ниже |
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | HEAD_DATA | byte[] | см. А.А.0 |
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[24,0,0,FLAGS2] | |
7 | PERSON ITEM | Все данные из инфо Person | см. таблицу ниже |
FLAGS2 & 1 - значит с одновременным удостоверением публичного ключа персоны
п/п | Наименование | Формат | Приводим к виду | Описание | Для подписи данных |
---|---|---|---|---|---|
1 | HEAD_DATA | byte[] | см. А.А.0 | ||
2 | birthdayBytes | byte[8] | long | + | |
3 | deathdayBytes | byte[8] | long | + | |
4 | Gender | byte[1] | int | + | |
5 | raceLength | byte[1] | int | + | |
6 | raceBytes | byte[raceLength] | String | + | |
7 | birthLatitude | byte[4] | float | + | |
8 | birthlongitude | byte[4] | float | + | |
9 | skinColorLength | byte[1] | int | + | |
10 | skinColor | byte[skinColorLength] | String | + | |
11 | eyeColorLength | byte[1] | int | + | |
12 | eyeColorBytes | byte[eyeColorLength] | String | + | |
13 | hairСolorLength | byte[1] | int | + | |
14 | hairСolorBytes | byte[hairСolorLength] | String | + | |
15 | Height | byte[1] | int | + | |
16 | ownerSignature | byte[64] | byte[] |
Колонка “для подписи данных” указывает на то какие поля собираются в байтовый массив для того чтобы подписать сами данные персоны и создать ownerSignature
Issue Status
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[25,0,0,0] | |
2 | STATUS_ITEM | byte[] | см. таблицу ниже |
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | HEAD_DATA | byte[] | см. А.А.0 |
Send Asset (R_Send)
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[31,VERS,0,0] | |
2 | RECIPIENT | byte[25] | byte[25] | |
3 | ASSET_KEY | byte[8] | long | не используется для писем |
4 | AMOUNT | byte[8] | BigDecimal | не используется для писем |
5 | HEAD LENGTH | byte[1] | byte[1] | |
6 | HEAD | byte[HEAD LENGTH] | String | |
7 | MESSAGE_LENGTH | byte[4] | long | |
8 | MESSAGE | byte[MESSAGE LENGTH] | String | |
9 | ENCRYPTED | byte[1] | int | |
10 | IS_TEXT | byte[1] | int |
TRANSACTION_TYPE[1] - VERS - номер версии транзакции. Сейчас задаем 2.
TRANSACTION_TYPE[2] - знаковый бит содержит флаг остутствия платежа. Если он поднят то это письмо. В этом случае поля ASSET_KEY и AMOUNT не участвуют в сборе кода транзакции, в том числе и для создания подписи (таблица ниже). Поднять этот флаг можно маской - (byte)128 или -(byte) -128
TRANSACTION_TYPE[2] - 6-й бит - флаг BACKWARD, если поднят то значит это транзакция возврата актива (маска 64).
TRANSACTION_TYPE[3] - знаковый бит содержит флаг остуствия сообщения. Если он поднят то, поля MESSAGE_LENGTH, MESSAGE, ENCRYPTED и IS_TEXT не нужно использовать (в том числе и в сборке для подписи).
В TRANSACTION_TYPE[3] заносится отличие в масштабе от стандартного (8 знаков после запятой), см приложение О.
Позиции балансов и возможные направления
Так же знаки у ASSET_KEY, AMOUNT и BACKWARD задают позицию баланса с которым работает транзакция.
Таблица A.31.Б. Виды баланcов и возможные направления (флаг BACKWARD)
п/п | Позиция баланса | знак ASSET_KEY | знак AMOUNT | BACKWARD | Направления |
---|---|---|---|---|---|
1 | OWN - в собственности | (+) | (+) | false | только прямое |
2 | DEBT - должен | (-) | (+) | fasle, true | оба направления (дать и забрать) |
3 | HOLD - на руках | (+) | (-) | true | только обратное |
4 | SPEND - потребил | (-) | (-) | false | только прямое |
5 | PLEDGE - залог | (+) | (+) | true | только обратное |
6 | RESERVED - зарезервировано | (+) | (-) | false | только прямое |
«+» Если залог передается на тот же счет с которого он берется, то это значит что ИМЕЮ у собственника уменьшается, а у передающего увеличивается - так залог переходит в собственность залогодержателя.
Например, если надо забрать долг (конфисковать у того кому ранее выдал), то ключ Актива - отрицательный, Количество - положительное и флаг BACKWARD = true.
Например, если нужно сделать транзакцию “беру на руки” (ACTION_HOLD) - то нужно сделать AMOUNT отрицательной величиной и взвести флаг BACWARD (так как у этого вида балансов возможно только обратное направление действия).
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[35, VERS, 0, 0] | |
2 | EX_DATA_LENGTH | int[4] | int[4] | Размер байт данных ExData |
3 | EX_DATA | byte[] | byte[] | Собирается из полей ExData, см. таблицу ниже |
4 | SIGNER (not used) |
Текущая версия (VERS) транзакции - 3, то есть:
TRANSACTION_TYPE[1]=3
TRANSACTION_TYPE[3] = -128 - значит есть дополнительные данные (EX_DATA). В Java (byte)-128 = 0x10000000
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | FLAGS | byte[4] | int | [VERS,0,0,0], сейчас VERS = 3 |
2 | TITLE LENGTH | byte[1] | int | |
3 | TITLE | byte[NAME LENGTH] | String UTF-8 | |
4 | IF FLAGS [1] = (byte)-128 - has external link (ExLink) - внешняя ссылка на родителя, значит собираем ExLink из следующих байтов (см. ниже таблицу) | |||
R | IF FLAGS [1] & 64 - has recipients - сбока байтов по реципиентам | |||
R.1 | RECIPIENTS FLAGS | byte[1] | byte | |
R.2 | RECIPIENTS LENGTH | byte[3] | int | |
R.3 | RECIPIENTs[][20] | byte[][20] | byte[][] | list of «short address» - 20 bytes. |
A | IF FLAGS [1] & 16 - есть авторы и мы их собираем (см. объект ExLink) | |||
A.1 | AUTHORS FLAGS | byte[1] | byte | |
A.2 | AUTHORS LENGTH | byte[3] | int | |
A.3 | AUTHORS_DATA | byte[] | byte[] | сборка по см. А 35.1.1 (Авторы) |
S | IF FLAGS [1] & 8 - есть источники и мы их собираем (см. объект ExLink) | |||
S.1 | SOURCES FLAGS | byte[1] | byte | |
S.2 | SOURCES LENGTH | byte[3] | int | |
S.3 | SOURCES_DATA | byte[] | byte[] | см. А 35.1.1 (Источники ) |
T | IF FLAGS [1] & 4 - есть метки и мы их собираем | |||
T.1 | TAGS LENGTH | byte | byte | длинна |
T.2 | TAGS | byte[] | byte[] | байты от строки UTF-8 Метки через запятую разделяются для быстрого поиска, например: “бизнес, продвижение, ООО Ромашка” |
C | IF FLAGS [1] & 32 - зашифровано и мы собираем пароли | |||
C.1 | SECRETS FLAGS | byte[1] | byte | reserved |
C.2 | SECRETS[][] где первый байт длинна секрета | byte[][] | byte[][] | Количество секретов + 1 число получателей. Причем последний секрет для отправителя. |
DD | DATA[] | byte[] | byte[] | остальные данные. Если зашифровано то как есть передается |
В нулевом байте FLAGS - версия ExData. текущая версия - 3.
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TYPE | byte | byte | Тип ExLink , см. ниже А.35.1.1 |
2 | FLAGS | byte | byte | флаги |
3 | VALUE_1 | byte | byte | значение 1 |
4 | VALUE_2 | byte | byte | значение 2 |
5 | SEQ_NO | byte[8] | long | ссылка на родителя как SeqNo - где 4 байта - номер блока и 4-ре байта - номер транзакции в нем |
6 | *MEMO LENGTH | byte | int | длинна примечания |
7 | *MEMO | byte[] | byte[] | байты из строки UTF-8 |
*Поля MEMO LENGTH и MEMO есть не у всех типов ExLink (см. ниже)
LINK - содержит номер транзакции в блокчейн в формате SeqNo : Номер блока + Номер записи в нем (int + int).
Причем если задается тип ссылки Приложение или Ответ - то необходимо в список Получателей добавить всех получателей и Создателя из Документа родителя убрав из полученного списка создателя текущего Документа (ответа или Приложения) - так что бы самому себе не слать этот Ответ. Это делается в приложении на стороне Клиента. Иначе если список будет пустой и тип Ответ - то по сути это Замечание получится.
№ | Наименование | Типы документа | Поля VALUE_1, VALUE_2 | Содержит MEMO_LENGTH и MEMO |
---|---|---|---|---|
0 | нет ссылки | Обычный, основной | нет | |
1 | APPENDIX | это Приложение, Дополнение | нет | |
2 | REPLY_COMMENT | это Ответ или Сообщение (Замечание). Если нет Получателей - то это Замечание (COMMENT) иначе - ответ (REPLY) | нет | |
5 | SURELY | Поручительство за другую инфо | нет | |
6 | SOURCE | Источник с долей/весом и примечанием | общее 2 байта - вес | да |
7 | AUTHOR | Автор с долей/весом и примечанием | общее 2 байта - вес | да |
Авторы и Источники - это ссылка в которой есть два добавочных поля MEMO_LENGTH и MEMO. Так чтобы в MEMO можно было записать заметку/примечание.
При этом у ссылок типа Авторы и Источники вместо 2-х значений по 1 байту есть одно значение в два байта, в которое можно заносить значение от 0 до 1000.
Длинна в байтах у ссылки Автор и Источник может быть переменной - зависит от примечания в нем.
Ссылка Автор - в качестве ссылки берется Номер Персоны.
Ссылка Источник - в качестве ссылки задается номер транзакции как 8 байт SeqNo.
Если поднят знаковый бит в флаге RECIPIENTS_FLAGS - это значит что могут подписать только получатели.
А.35.3.1 Авторы и Источники.
Сборка объекта ExLink из AUTHORS_DATA и SOURCES_DATA смотри таблицу А.35.1
Используется для задания списка наград в Активах.
Сборка объекта «Счет».
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TYPE | byte | byte | = 0 |
2 | FLAGS | byte | byte | флаги = 0 |
3 | VALUE_1 | byte[4] | int | значение 1 |
4 | VALUE_2 | byte[4] | int | значение 2 |
5 | ADDRESS | byte[20] | byte[20] | короткое представление счета |
6 | MEMO LENGTH | byte | int | длинна примечания |
7 | MEMO | byte[] | byte[] | байты из строки UTF-8 |
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | JSON_LENGTH | byte[4] | int | [0,0,0,0] |
2 | JSON[] | byte[JSON_LENGTH] | byte[] | UTF-8 |
3 | FILES[][] | byte[][] | bete[][] | bytes - сборка файлов по данным из JSON, см. ниже |
Соответственно после JSON_LENGTH идут данные для файлов. Файлы парсятся по данным в JSON, см.параметр F ниже.
Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|
MS | String | String | сообщение в свободной форме |
MSU | boolean | если да - то уникальное сообщение (создается хэш и вносится в базу данных блокчейн для поиска) | |
TM | long | long | Ключ шаблона |
TMU | boolean | если да - то уникальный шаблон с параметрами (создается хэш от номера шаблона и значений параметров и вносится в базу данных блокчейн для поиска) | |
PR | JSON | JSON | Список параметров для шаблона |
HS | JSON | JSON | список Хэшей, где ключ - ХЭШ, а значение - путь к файлу. Эти хэши никак не соотносятся с файлами в параметре «F» - у файлов свои отдельные хэши, которые вычисляются налету и не хранятся в блокчейне. |
HSU | boolean | если да - то каждый хэш уникальный и он вносится в базу данных блокчейн для поиска | |
F (old vers) устарело - не используется, новый формат ниже | JSON | JSON | ключ - номер файла. Значение: FN - имя файла ZP - boolean - сжат SZ - размер в байтах в данных |
F | Array | Array | FN - имя файла (String) ZP - boolean - сжат SZ - размер в байтах в данных, см. состав DATA выше. У файлов так же создаются хэши но не сохраняются никуда. Эти хэши просто показываются пользователям для информации. |
FU | boolean | если да - то каждый файл уникальный и хэш от него вносится в базу данных блокчейн для поиска с проверкой уникальности. |
Подпись как и у всех - данные транзакции без подписи + Порт (или подпись для сайдчейнов).
Если поднять флаг Зашифровано, то размер списка секретов будет равен длине списка получателей +1. Причем последний элемент - это секрет для самого создателя. Каждый секрет это набор байт где первый байт его длинна и потом в байтах сам секрет.
Данные DATA (поле DD в таб. А.35.0) зашифровываются одни общим секретом - в 32 байта, который создается с помощью CryproRundom().
Далее этот Общий секрет документа еще раз зашифровывается для каждой пары Отправитель-Получатель (это Секрет Пары) - так же как и сообщения в письмах шифруются.То есть Общий секрет - это случайная величина из 32 байт - для ее создания используем криптографический SecureRandom - для обеспечения высокой случайности. Для создания Секрета Пары для самого создателя документа берем его приватный ключ и его публичный ключ.
Функция для создания хэшей - SHA256.
Хэш сообщения - необходимо его преобразовать в байт-код из UTF-8 раскладки. Вот пример кода на Java:
digest(message.getBytes(StandardCharsets.UTF_8));.
Хэш для шаблона:
digest(("" + templateKey
+ params.toJSONString()).getBytes(StandardCharsets.UTF_8));
Для создания Хэша из файла:
код тут - org.erachain.utils.FileHash - большие файлы разрезаются на 1МБ и отдельно хэшируются.
Certify Person Transaction
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[36,0,1,0] 1 - один адрес привязывается, можно привязать до 256 | |
2 | PERSON KEY | byte[8] | long | |
3 | Account PUBLIC KEY | byte[32] | byte[32] | |
4 | DAY | byte[4] | int |
Set Status To Item Transaction
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[37,0,0, Params] | |
2 | KEY STATUS | byte[8] | long | |
3 | ITEM TYPE | byte[1] | int | |
4 | KEY ITEM | byte[8] | long | |
5 | DATE START | byte[8] | long | |
6 | DATE END | byte[8] | long | |
7 | VALUE1 если не ноль, то нужно поднять флаг в 0-м бите 4-го байта TRANSACTION_TYPE | byte[8] | long | флаг в 0-й бит TRANSACTION_TYPE[3] |
8 | VALUE2 если не ноль, то нужно поднять флаг в 1-м бите 4-го байта TRANSACTION_TYPE | byte[8] | long | флаг в 1й бит TRANSACTION_TYPE[3] |
9 | DATA1 LENGTH | byte[1] | int | |
10 | DATA | byte[DATA1 LENGTH] | String | флаг в 2й бит TRANSACTION_TYPE[3] |
11 | DATA2 LENGTH | byte[1] | int | |
12 | DATA | byte[DATA2 LENGTH] | String | флаг в 3й бит TRANSACTION_TYPE[3] |
13 | REF TO PARENT | byte[8] | long | флаг в 4й бит TRANSACTION_TYPE[3] |
14 | DESCRIPTION | byte[] | String | флаг в 5й бит TRANSACTION_TYPE[3] |
Примечание: если какое-либо из дополнительных значений не ноль, или не Null, то нужно поднять соответствующий флаг как описано в колонке Примечания. Например если задано Описание (DESCRIPTION), то нужно сделать: TRANSACTION_TYPE[3] | 32.
R_Vouch
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[40,0,0,0] | |
2 | BLOCK HEIGHT | byte[4] | int | |
3 | TRANSACTION NUMBER IN BLOCK | byte[4] | int |
Примечание: вместе байты номера блока и номера транзакции в нем создают ссылку на транзакцию вида SeqNo (Long).
URL Hash
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[41,0,0,0] Третий и четвертый байт - кол-во хешей в транзакции | |
2 | REFERENCE | byte[8] | long | [0,0,0,0,0,0,0,0] |
3 | URL LENGTH | byte[1] | int | |
4 | URL | byte[URL LENGTH] | String | |
5 | DATA_SIZE_LENGTH | byte[4] | int | |
6 | DATA | byte[DATA_SIZE_LENGTH] | String | |
7 | Hashes[i] | byte[32][i] | byte[32] | i - количество записываемых HASH . Длина hash - 32 байта |
Create Order
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[50,0, X1, X2] | |
2 | HAVE_ASSET | byte[8] | long | Код Актива, который имеем |
3 | WANT_ASSET | byte[8] | long | Код Актива, который хотим |
4 | HAVE_AMOUNT | byte[8] | BigDecimal | Кол-во, которое имеем |
5 | WANT_AMOUNT | byte[8] | BigDecimal | Кол-во, которое хотим |
Для преобразования BigDecimal в byte[8] - в TRANSACTION_TYPE заносится отличие в масштабе от стандартного (8 знаков после запятой) для HAVE_AMOUNT и для WANT_AMOUNT в 3-й (Х1) и 4-й (Х2) байты соответственно, см приложение О.
Order Status
п/п | Наименование | Число | Описание |
---|---|---|---|
1 | UNCONFIRMED | 0 | есть в очереди ожидания транзакция с созданием этого ордера |
2 | ACTIVE | 1 | неисполненный, еще в стакане на бирже |
3 | FULFILLED | 2 | частично исполненный ордер |
4 | COMPLETED | 3 | исполненный полностью |
5 | CANCELED | 4 | отмененный |
6 | ORPHANED | -1 | нет нигде |
Cancel Order
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[51,0,0,0] | |
2 | ORDER_SIGNATURE | byte[64] | Подпись транзакции выпустившей отменяемый ордер |
Change Order
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | TX_HEAD | см.А.0 | head=[52,0,0,VAR3] | |
2 | ORDER_SIGNATURE | byte[64] | Подпись транзакции выпустившей изменяемый ордер | |
3 | NEW_AMOUNT | byte[8] | BigDecimal | Новое кол-во ХОЧУ или ИМЕЮ (см. флаг в VAR3) |
VAR3 & 32 > 0 - значит изменение касается количества ИМЕЮ
VAR3 & 31 - значение для преобразования BigDecimal в byte[8] - в TRANSACTION_TYPE заносится отличие в масштабе от стандартного (8 знаков после запятой)
см. приложение О.
Структура данных блока
Block
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | VERSION | byte[4] | int | current version 1 |
2 | REFERENCE | byte[64] | byte[64] | |
3 | CREATOR PUBLIC KEY(GENERATOR) | byte[32] | byte[32] | |
4 | CREATOR BALANCE | byte[4] | int | |
5 | TRANSACTION HASH | byte[32] | byte[32] | |
6 | SIGNATURE | byte[64] | byte[64] | |
7 | TRANSACTIONS COUNT | byte[4] | int | |
8 | TRANSACTIONS | byte[from last position to end] | byte[from last position to end] |
Signature
п/п | Наименование | Формат | Приводим к виду | Описание |
---|---|---|---|---|
1 | VERSION | byte[4] | int | текущая версия 1 |
2 | REFERENCE | byte[64] | byte[64] | |
3 | TRANSACTION HASH | byte[32] | byte[32] |
Для сохранения BigDecimal в базе данных или создания байт-кода транзакций.
Пример для создания Ордера, точность записывается в байты Типа:
BlockChain.AMOUNT_DEDAULT_SCALE = 8;
TransactionAmount.SCALE_MASK = 31;
// WRITE ACCURACY of AMOUNT HAVE
int different_scale = this.amountHave.scale() - BlockChain.AMOUNT_DEDAULT_SCALE;
BigDecimal amountBase;
if (different_scale != 0) {
// RESCALE AMOUNT
amountBase = this.amountHave.scaleByPowerOfTen(different_scale);
if (different_scale < 0)
different_scale += TransactionAmount.SCALE_MASK + 1;
data[2] = (byte) (data[2] | different_scale);
} else {
amountBase = this.amountHave;
}
// WRITE AMOUNT HAVE
byte[] amountHaveBytes = amountBase.unscaledValue().toByteArray();
byte[] fill_H = new byte[AMOUNT_LENGTH - amountHaveBytes.length];
amountHaveBytes = Bytes.concat(fill_H, amountHaveBytes);
data = Bytes.concat(data, amountHaveBytes);
// WRITE ACCURACY of AMOUNT WANT
different_scale = this.amountWant.scale() - BlockChain.AMOUNT_DEDAULT_SCALE;
if (different_scale != 0) {
// RESCALE AMOUNT
amountBase = this.amountWant.scaleByPowerOfTen(different_scale);
if (different_scale < 0)
different_scale += TransactionAmount.SCALE_MASK + 1;
data[3] = (byte) (data[3] | different_scale);
} else {
amountBase = this.amountWant;
}
// WRITE AMOUNT WANT
byte[] amountWantBytes = amountBase.unscaledValue().toByteArray();
byte[] fill_W = new byte[AMOUNT_LENGTH - amountWantBytes.length];
amountWantBytes = Bytes.concat(fill_W, amountWantBytes);
data = Bytes.concat(data, amountWantBytes);
Документ содержит описание формирования публичного и секретного ключа на PHP, JS. Реализация формирования ключей на PHP, JS.
Криптографические алгоритмы ERA используют криптоалгоритмы на эллиптических кривых и ED25519 для электронной подписи транзакций.
Curve25529 для выработки общего секрета в алгоритме Elliptic-curve Diffie-Hellman (ECDH).
https://en.wikipedia.org/wiki/Curve25519
https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman
Для шифрования используется AES256 шифрование.
СИД - 32 байта секретной информации, из которой генерируется последовательность сидов ключевых пар:
Ключевая пара - пара ключей, секретного и публичного, необходимых для создания подписи.
СИД ключевой пары - данные из которых создается ключевая пара.
Версионные байты - массив байт размером 1 или 2 байта, в которых записаны версии криптографии, в которых:
Версионный СИД пары - СИД пары, превышающий по длине 32 байта, в котором первые байты есть версионные байты.
Для передачи версионности используются 1 или 2 байта - версионные байты, которые изначально присутствуют в мастр-сиде, потом переходят в сид ключевой пары, а затем в публичный ключ. При этом, при вызове стандартных методов криптографии, версионные ключи изымаются, так чтобы на входе в криптографическую библиотеку заходил стандартный мастер-сид (32 байта), сид ключевой пары (64 байта) и публичный ключ (32 байта). После получения результатов, если необходимо, версионные байты добавляются к началу результирующего массива байт. Например при генерации ключевой пары из версионного сида ключевой пары, из него изымаются версионные байты, остаток передается в обработку, а к результату прибавляются версионные байты в начало массива байт и уже совокупность выдается как результат работы.
Изначально есть мастер-сид (byte[32..34]) и отступ (int), который начинают с 1
org.erachain.core.wallet.Wallet#generateAccountSeed(byte[] seed, byte[] nonce)
Пример:
byte[] digestNonce;
switch (seedAndVersion.length) {
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 2: {
digestNonce = Bytes.concat(nonceBytes,
Arrays.copyOfRange(seedAndVersion, 2, seedAndVersion.length),
nonceBytes);
}
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 1: {
digestNonce = Bytes.concat(nonceBytes,
Arrays.copyOfRange(seedAndVersion, 1, seedAndVersion.length),
nonceBytes);
}
default:
digestNonce = Bytes.concat(nonceBytes,
seedAndVersion,
nonceBytes);
}
digestNonce = Crypto.getInstance().doubleDigest(digestNonce);
switch (seedAndVersion.length) {
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 2: {
return Bytes.concat(Arrays.copyOf(seedAndVersion, 2), digestNonce);
}
case PublicKeyAccount.PUBLIC_KEY_LENGTH + 1: {
return Bytes.concat(Arrays.copyOf(seedAndVersion, 1), digestNonce);
}
default:
return Crypto.getInstance().doubleDigest(digestNonce);
}
Изначально есть сид пары (byte[32..34]) с первыми байтами версии криптографии (версионный сид пары). Из версионного сида пары вычленяют первые байты, если размер его превышает 32 байта, и получают сид пары размером 32 байта. Далее из него генерируют ключевую пару описанным ниже методом. После получения пары к публичному ключу добавляют версионные байты (1 или 2 в зависимости от длины версионного сида на входе).
Генерация новой ключевой пары происходит по следующему алгоритму:
Репозиторий
Для формирования ключа используются библиотеки:
Алгоритм работы
Изначальные данные - сид из которого формируется ключевая пара.
Листинг:
const sha512 = require('sha512')
var sodiumSignatures = require('sodium-signatures')
const libbase64 = require('libbase64');
var seed = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
console.log("seed: " + seed);
var seed_decode = libbase64.decode(seed);
var sha512_seed = sha512(seed_decode);
var sha512_seed_decode = libbase64.encode(sha512_seed)
console.log("seed after sha512: " + sha512_seed_decode);
var seed_keyPair = sodiumSignatures.keyPair(seed_decode)
var seed_keyPair_public = seed_keyPair.publicKey;
var seed_keyPair_private = seed_keyPair.secretKey;
console.log("public key: " + libbase64.encode(seed_keyPair_public));
console.log("private key: " + libbase64.encode(seed_keyPair_private));
seed_keyPair_private_part1 = (seed_keyPair.secretKey).slice(0, 32);
seed_keyPair_private_part2 = (seed_keyPair.secretKey).slice(32, 64);
console.log("first 32 byte: " + libbase64.encode(seed_keyPair_private_part1))
console.log("last 32 byte: " + libbase64.encode(seed_keyPair_private_part2))
seed_keyPair_private_sha512_part1 = sha512(seed_keyPair_private_part1)
console.log("sha512 first part by private key: " + libbase64.encode(seed_keyPair_private_sha512_part1));
var sc=seed_keyPair_private_sha512_part1
var firstByte = sc[0];
firstByte &= 248;
sc[0] = firstByte;
var lastByte = sc[31]
lastByte &= 127
lastByte |= 64
sc[31]=lastByte;
console.log("secret key after mascing: " + libbase64.encode(sc));
Используемые библиотеки и ОС на которой производилось тестирование:
Тестирование производилось на PHP 7.2 на Windows.
Необходимо подключить расширения в PHP (php.ini)
extension=php_sodium.dll
extension=php_openssl.dll
Пример 1
Генерация ключей в Sodium PHP и конвертирование в формат ERA:
Код PHP:
<?php
echo "--- SODIUM CRYPTO SIGN ---\n";
$bob_seed_base64 = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
echo "seed sodium base64: " . $bob_seed_base64 ."\n";
$bob_seed = base64_decode($bob_seed_base64);
$sha512_bob_seed = hash('sha512',$bob_seed , true);
echo "sha512(seed) base64: " .base64_encode($sha512_bob_seed) . "\n";
$bob_sha = $sha512_bob_seed;
$bob_encrypt_kp = sodium_crypto_sign_seed_keypair($bob_seed);
//echo "keypair sodium crypto sign base64: " . base64_encode($bob_encrypt_kp) . "\n";
$bob_box_publickey = sodium_crypto_sign_publickey($bob_encrypt_kp);
$bob_box_secretkey = sodium_crypto_sign_secretkey($bob_encrypt_kp);
echo "Public key sodium crypto sign base64: " . base64_encode($bob_box_publickey) . "\n";
echo "Private key sodium crypto sign base64: " . base64_encode($bob_box_secretkey) . "\n";
echo "So format of Sodium Private key is seed_32_bytes & public_key_32_Bytes !!!: " ."\n";
// format of ERA Secret Key is: 64 bytes sha512(seed) and masking of 0 and 31 bytes
// Let's try to convert Sodium Secret key to Era Secret key
$bob_box_era_secretkey = substr($bob_box_secretkey , 0,32 );
echo "first 32 bytes of Private key sodium base64: " . base64_encode($bob_box_era_secretkey) . "\n";
$bob_box_era_secretkey2 = substr($bob_box_secretkey , 32,32 );
echo "last 32 bytes of Private key sodium base64: " . base64_encode($bob_box_era_secretkey2) . "\n";
$bob_box_era_secretkey = hash('sha512',$bob_box_era_secretkey , true);
echo "sha512 first 32 bytes of Private key sodium base64: " . base64_encode($bob_box_era_secretkey) . "\n";
// masking first and last bytes
$first_byte = ord($bob_box_era_secretkey[0]);
$first_byte &= 248;
$bob_box_era_secretkey[0] = chr($first_byte);
$last_byte = ord($bob_box_era_secretkey[31]);
$last_byte &= 63;
$last_byte |= 64;
$bob_box_era_secretkey[31] = chr($last_byte);
echo "After masking 0 and 31 byte here is converted Sodium Private key to ERA format base64: \n". base64_encode($bob_box_era_secretkey) . "\n";
echo"--- JAVA ERA to compare ---\n";
// result from java ERA code for control
echo "Seed java base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=" . "\n";
echo "Public key java base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=" . "\n";
echo "Private key java base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==". "\n";
?>
Результат:
--- SODIUM CRYPTO SIGN ---
seed sodium base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
sha512(seed) base64: nuGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
Public key sodium crypto sign base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Private key sodium crypto sign base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt7FTz3TmcdYiqLT3PBuPer6VMEbNKPEOzLyq5bGWEsheg==
So format of Sodium Private key is seed_32_bytes & public_key_32_Bytes !!!:
first 32 bytes of Private key sodium base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
last 32 bytes of Private key sodium base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
sha512 first 32 bytes of Private key sodium base64: nuGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
After masking 0 and 31 byte here is converted Sodium Private key to ERA format base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
--- JAVA ERA to compare ---
Seed java base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
Public key java base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Private key java base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
Пример 2
Получение общего секрета в Sodium PHP и сравнение с ERA:
Код PHP:
<?php
echo "--- Shared secret ---\n";
//--- Generate Bob's keypair
$bob_seed_base64 = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
echo "Bob seed sodium base64: " .$bob_seed_base64 . "\n";
$bob_seed = base64_decode($bob_seed_base64);
$bob_encrypt_kp = sodium_crypto_sign_seed_keypair($bob_seed);
$bob_publickey = sodium_crypto_sign_publickey($bob_encrypt_kp);
$bob_secretkey = sodium_crypto_sign_secretkey($bob_encrypt_kp);
echo "Bob Public key sodium crypto sign base64: " . base64_encode($bob_publickey) . "\n";
echo "Bob Private key sodium crypto sign base64: " . base64_encode($bob_secretkey) . "\n";
//--- Generate Alice's keypair
$alice_seed_base64 = "1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=";
echo "Alice seed sodium base64: " .$alice_seed_base64 . "\n";
$alice_seed = base64_decode($alice_seed_base64);
$alice_encrypt_kp = sodium_crypto_sign_seed_keypair($alice_seed);
$alice_publickey = sodium_crypto_sign_publickey($alice_encrypt_kp);
$alice_secretkey = sodium_crypto_sign_secretkey($alice_encrypt_kp);
echo "Alice Public key sodium crypto sign base64: " . base64_encode($alice_publickey) . "\n";
echo "Alice Private key sodium crypto sign base64: " . base64_encode($alice_secretkey) . "\n";
//--- Convert SODIUM CRYPTO SIGN ED25519 TO CURVE25529 ---
echo "--- Convert SODIUM CRYPTO SIGN keys ED25519 TO CURVE25529 ---\n";
$bob_curve_pk = sodium_crypto_sign_ed25519_pk_to_curve25519 ( $bob_publickey);
echo "Bob Public key sodium curve25519 base64: " . base64_encode($bob_curve_pk) . "\n";
$bob_curve_sk = sodium_crypto_sign_ed25519_sk_to_curve25519 ( $bob_secretkey );
echo "Bob Private key sodium curve25519 base64: " . base64_encode($bob_curve_sk) . "\n";
$alice_curve_pk = sodium_crypto_sign_ed25519_pk_to_curve25519 ( $alice_publickey);
echo "Alice Public key sodium curve25519 base64: " . base64_encode($alice_curve_pk) . "\n";
$alice_curve_sk = sodium_crypto_sign_ed25519_sk_to_curve25519 ( $alice_secretkey);
echo "Alice Private key sodium curve25519 base64: " . base64_encode($alice_curve_sk) . "\n";
echo "--- Generate DH shared secret ---\n";
// sodium_crypto_scalarmult($secretkey1, $publickey2);
// sodium_crypto_scalarmult($secretkey2, $publickey1);
$ss1 = sodium_crypto_scalarmult($bob_curve_sk,$alice_curve_pk);
echo "Shared secret on Bob side (Bob-Alice) base64: ".base64_encode($ss1)."\n";
$ss2 = sodium_crypto_scalarmult($alice_curve_sk,$bob_curve_pk);
echo "Shared secret on Alice side (Alice-Bob) base64: ".base64_encode($ss2)."\n";
echo "--- JAVA ERA to compare ---\n";
// result from java ERA code for control
echo "Account 1 base64 Seed: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=" . "\n";
echo "Account 1 base64 Public key: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=" . "\n";
echo "Account 1 base64 Private key: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==". "\n";
echo "Account 2 base64 Seed: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=" . "\n";
echo "Account 2 base64 Public key: OiQUzvZ4kknpZTbCPghnE78jTSzdi6+kBHsa7OTvJAc=" . "\n";
echo "Account 2 base64 Private key: +G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XZS917QL+G1meSpvHFQBSnnasAvAzFGqTwJu/e5Jk0FKA==". "\n";
echo "Shared secret 1-2 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=" . "\n";
echo "Shared secret 2-1 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=" . "\n";
Результат:
--- Shared secret ---
Bob seed sodium base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
Bob Public key sodium crypto sign base64: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Bob Private key sodium crypto sign base64: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt7FTz3TmcdYiqLT3PBuPer6VMEbNKPEOzLyq5bGWEsheg==
Alice seed sodium base64: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=
Alice Public key sodium crypto sign base64: OiQUzvZ4kknpZTbCPghnE78jTSzdi6+kBHsa7OTvJAc=
Alice Private key sodium crypto sign base64: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ6JBTO9niSSellNsI+CGcTvyNNLN2Lr6QEexrs5O8kBw==
--- Convert SODIUM CRYPTO SIGN keys ED25519 TO CURVE25529 ---
Bob Public key sodium curve25519 base64: 9oVonTcqGTVWTb17g+Gp8LgEgXCD64NVQAPammJ/8yA=
Bob Private key sodium curve25519 base64: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClw=
Alice Public key sodium curve25519 base64: r7UwQh5XUOB2W8AwYoig0qgXUUxIZb3MaxFrUcswkA0=
Alice Private key sodium curve25519 base64: +G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XY=
--- Generate DH shared secret ---
Shared secret on Bob side (Bob-Alice) base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
Shared secret on Alice side (Alice-Bob) base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
--- JAVA ERA to compare ---
Account 1 base64 Seed: iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=
Account 1 base64 Public key: xU8905nHWIqi09zwbj3q+lTBGzSjxDsy8quWxlhLIXo=
Account 1 base64 Private key: mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClzXrZe8ywIGYLuqsZOOoptMsTiZVyqsVAJ31Ms5adj3Gg==
Account 2 base64 Seed: 1QK+er2p+qdTulnaVez1q1fGATd0ta++g48UdnDd9dQ=
Account 2 base64 Public key: OiQUzvZ4kknpZTbCPghnE78jTSzdi6+kBHsa7OTvJAc=
Account 2 base64 Private key: +G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XZS917QL+G1meSpvHFQBSnnasAvAzFGqTwJu/e5Jk0FKA==
Shared secret 1-2 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
Shared secret 2-1 base64: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
Пример 3
Шифрование OpenSSL PHP и сравнение с ERA:
Код PHP:
<?php
//Already converted to Curve25519 format
$pk1 = base64_decode("9oVonTcqGTVWTb17g+Gp8LgEgXCD64NVQAPammJ/8yA=");
$sk1 = base64_decode("mOGNO6WsNqhOl+J8wbUF7CgMBvzzGJr4eKP0ldMNClw=");
$pk2= base64_decode("r7UwQh5XUOB2W8AwYoig0qgXUUxIZb3MaxFrUcswkA0=");
$sk2 = base64_decode("+G6rh4MKI0krAQDzaVMWxPijhBQJq3XmR3tc7HwK6XY=");
// sodium_crypto_scalarmult($secretkey1, $publickey2);
// sodium_crypto_scalarmult($secretkey2, $publickey1);
$ss1 = sodium_crypto_scalarmult($sk1,$pk2);
echo "pk1-sk2: ".base64_encode($ss1)."\n";
$ss2 = sodium_crypto_scalarmult($sk2,$pk1);
echo "pk2-sk1: ".base64_encode($ss2)."\n";
// try encrypt with openssl aes-256-cbc
$plaintext = '12345';
$method = 'aes-256-cbc';
// password in ERA is sha256(shared secret).
// Must be exact 32 chars (256 bit) sha 256 from sharedsecret like ERA algoritm
$password = substr(hash('sha256',$ss1, true),0, 32);
echo "password base64: ".base64_encode($password)."\n";
// IV constant must be exact 16 chars (128 bit)
// Value of IV constant from ERA code {6, 4, 3, 8, 1, 2, 1, 2, 7, 2, 3, 8, 5, 7, 1, 1}
$iv = chr(0x06) . chr(0x04) . chr(0x03) . chr(0x08) . chr(0x01) . chr(0x02) . chr(0x01) . chr(0x02) . chr(0x07) . chr(0x02) . chr(0x03) . chr(0x08) . chr(0x05) . chr(0x07) . chr(0x01) . chr(0x01);
echo "iv= ".base64_encode($iv) . "\n";
$encrypted = openssl_encrypt($plaintext,$method, $password,OPENSSL_RAW_DATA, $iv);
// My secret message
$decrypted = openssl_decrypt($encrypted,$method, $password,OPENSSL_RAW_DATA, $iv);
echo 'plaintext= ' . $plaintext . "\n";
echo 'plaintext base64 = ' .base64_encode($plaintext) . "\n";
echo 'cipher= ' . $method . "\n";
echo 'encrypted base64: ' . base64_encode($encrypted) . "\n";
echo 'encrypted hex: ' . bin2hex($encrypted) . "\n";
// For ERA first byte should be added to encrypted data. It is encryption method type = 1
$era_encrypted = chr(0x01) . $encrypted;
echo 'era encrypted base64: ' .base64_encode($era_encrypted) . "\n";
echo 'era encrypted hex: ' .bin2hex($era_encrypted) . "\n";
// java result
echo "--- Java results ---"."\n";
echo "Java plaintext= 12345"."\n";
echo "Java password base64: KPOo4BgJwpcG5O6E+UZ+Fr0ADaD3BPZeR6+3p6MmXs0="."\n";
echo "Java Message bytes base64: MTIzNDU=" . "\n";
echo "Java Encrypted result base64: ASqcvML2g7HzjZ6Lbk2Dq58=" . "\n";
echo "Java Encrypted result hex: 012a9cbcc2f683b1f38d9e8b6e4d83ab9f" . "\n";
echo "Java Result decrypt: 12345" ."\n";
// -- Test decrypting message from ERA
echo "--- Decrypt message from ERA ---" . "\n";
$era_encrypted_base64 = "ASCCq4KtYUJyw4Ax8Jj19O0XnqOe25XTOgvh6GYlz8LgHa6VKAjx+XKZdPgWVlnStw==";
echo "Java Encrypted message base64: " . $era_encrypted_base64 ."\n";
$era_encrypted = base64_decode($era_encrypted_base64);
// cut off first byte from ERA encrypted data
$encrypted = substr($era_encrypted,1, strlen($era_encrypted)-1);
// prepare arguments
$password = substr(hash('sha256',$ss1, true),0, 32);
$method = 'aes-256-cbc';
$iv = chr(0x06) . chr(0x04) . chr(0x03) . chr(0x08) . chr(0x01) . chr(0x02) . chr(0x01) . chr(0x02) . chr(0x07) . chr(0x02) . chr(0x03) . chr(0x08) . chr(0x05) . chr(0x07) . chr(0x01) . chr(0x01);
$decrypted = openssl_decrypt($encrypted,$method, $password,OPENSSL_RAW_DATA, $iv);
echo "Decrypted message: " .$decrypted . "\n";
Результат:
pk1-sk2: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
pk2-sk1: i+gj6Xb22VI8LuG3hQCEorNLNXhxDa4Vo+5t/kuQthA=
password base64: KPOo4BgJwpcG5O6E+UZ+Fr0ADaD3BPZeR6+3p6MmXs0=
iv= BgQDCAECAQIHAgMIBQcBAQ==
plaintext= 12345
plaintext base64 = MTIzNDU=
cipher= aes-256-cbc
encrypted base64: Kpy8wvaDsfONnotuTYOrnw==
encrypted hex: 2a9cbcc2f683b1f38d9e8b6e4d83ab9f
era encrypted base64: ASqcvML2g7HzjZ6Lbk2Dq58=
era encrypted hex: 012a9cbcc2f683b1f38d9e8b6e4d83ab9f
--- Java results ---
Java plaintext= 12345
Java password base64: KPOo4BgJwpcG5O6E+UZ+Fr0ADaD3BPZeR6+3p6MmXs0=
Java Message bytes base64: MTIzNDU=
Java Encrypted result base64: ASqcvML2g7HzjZ6Lbk2Dq58=
Java Encrypted result hex: 012a9cbcc2f683b1f38d9e8b6e4d83ab9f
Java Result decrypt: 12345
--- Decrypt message from ERA ---
Java Encrypted message base64: ASCCq4KtYUJyw4Ax8Jj19O0XnqOe25XTOgvh6GYlz8LgHa6VKAjx+XKZdPgWVlnStw==
Decrypted message: Это секретное сообщение
Пример 4
Генерация и проверка подписи в Sodium PHP и сравнение с ERA:
Код PHP:
<?php
echo "--- SODIUM SIGNAGE ---"."\n";
// $sign_seed = random_bytes(SODIUM_CRYPTO_SIGN_SEEDBYTES);
// $sign_pair = sodium_crypto_sign_seed_keypair($sign_seed);
$seed_base64 = "iGT1jhHFElSM1WGX6cOg1jRImq4Y4UQdGvAgRdUiDt4=";
$sign_pair = sodium_crypto_sign_seed_keypair(base64_decode($seed_base64));
$sign_secret = sodium_crypto_sign_secretkey($sign_pair);
$sign_public = sodium_crypto_sign_publickey($sign_pair);
//--------------------------------------------------
// Signing
$message = 'Hello';
echo "--- Signaning in Sodium ---" ."\n";
$signature = sodium_crypto_sign_detached($message,$sign_secret);
echo "Message: " .$message . "\n";
echo "Sodium signature of message base64: " . base64_encode($signature) . "\n";
//--- Java result to compare ----
echo "Java message to sign: " ."Hello". "\n";
echo "Java Account 1 create signature base64: owVT9t8OOkN3mZXrDuZtmBAKFgSydeYwFy7qaoZ7Gb4TKrggBZ8SroJcXttdgNtoj43XthJQIZvToDjCVsCg==". "\n";
echo "Java Result account 1 validation signature by public key account 1: true" . "\n";
//--------------------------------------------------
// Verifying
echo "--- Verifing in Sodium: " .$message . "\n";
//Java message: Проверь мою подпись
//Java signature: wl7/LHiRc6LSDJqJxCt0pY9rqRcFj7vxGhOGRhaBjPCaWm5K0VeqYiaAE5sNd8gxOA9h9sx79RZhCrRLIdgiAA==
$message = "Проверь мою подпись";
$signature_base64 = "wl7/LHiRc6LSDJqJxCt0pY9rqRcFj7vxGhOGRhaBjPCaWm5K0VeqYiaAE5sNd8gxOA9h9sx79RZhCrRLIdgiAA==";
$signature = base64_decode($signature_base64);
echo "Message signed in Java: " .$message . "\n";
echo "Signature of message in Java base64: " . base64_encode($signature) . "\n";
$message_valid = sodium_crypto_sign_verify_detached($signature, $message,$sign_public);
echo "Sodium signature validation result: " . $message_valid . "\n";
if (!$message_valid) {
exit('Message has been changed.');
}
?>