Фабрика документов
Электронные документы в кооперативе — это юридически-значимые действия. Для них важны одинаковая форма, повторяемость и проверяемость. Фабрика автоматизирует процесс и делает его повторяемым: каждый документ создаётся по одному маршруту, на основе шаблона, версии шаблона и исходных данных, которые можно перепроверить. Это защищает от расхождений между версиями, гарантирует, что контрольная сумма и подпись относятся к тому же содержимому, и самое главное - позволяет автоматизировать работу документами в стандартизированных бизнес-процессах кооператива.
Электронный документ¶
Электронный документ — файл, который содержит юридически-значимое действие пайщика. Он создан по общему кооперативному стандарту, может быть подписан и передан.
Электронный документ в кооперативной экономике может находится в двух математически-связанных между собой состояниями: полная форма и подписанная форма.
Полная форма документа включает в себя "сырые" данные, доступные для отображения или скачивания в виде PDF-файла. Это тот вид документа, который мы привыкли видеть, и который содержит все данные, которые в нем должны быть - все заполненные поля, доступные для чтения, и т.д..
Интерфейс данных полного документа:
interface GeneratedDocument {
binary: <string>; // Бинарное содержимое документа (base64)
full_title: <string>; // Полное название документа
hash: <string>; // Хэш документа
html: <string>; // HTML содержимое документа
meta: <ModelTypes["JSON"]>; // Метаданные документа
}
Подписанная форма документа включает в себя контрольную сумму полной формы, следовательно, они математически-взаимосвязаны - из полной формы всегда можно получить контрольную сумму подписанной формы и произвести сверку. В подписанной форме всегда есть контрольная сумма полного документа и мета-данные, которых достаточно для того, чтобы установить того, кто сгенерировал документ, и того, кто его подписал, и сделать это математически-точно и без публичного раскрытия персональных данных.
Интерфейс данных подписанного документа:
interface SignedBlockchainDocument {
doc_hash: <string>; // Хэш содержимого документа
hash: <string>; // Общий хэш (doc_hash + meta_hash)
meta: <string>; // Метаинформация документа (в формате JSON-строки)
meta_hash: <string>; // Хэш мета-данных
signatures: <ModelTypes["SignatureInfo"][]>; // Вектор подписей
version: <string>; // Версия стандарта документа
}
Обратное же, т.е. получить полную форму из подписанной может только тот, у кого есть доступ к закрытой информации в базе данных кооператива - обычно это или пайщик, кому принадлежит документ, или член совета.
Для передачи подписей в документах используется следующий интерфейс:
interface SignatureInfoInput {
id: <number>; // Идентификатор номера подписи
meta: <string>; // Мета-данные подписи
public_key: <string>; // Публичный ключ
signature: <string>; // Подпись хэша
signed_at: <string>; // Время подписания
signed_hash: <string>; // Подписанный хэш
signer: <string>; // Аккаунт подписавшего
}
Он позволяет накладывать на документ и поверх предыдущих подписей неограниченное количество новых подписей. Обычно, нам в наших бизнес-процессах не требуется больше двух подписей на одном документе (например, на акте приёма-передачи требуется две подписи). Но для исключительных случаев мы не ограничены в количестве подписей на одном документе.
Проверка подписей осуществляется на уровне смарт-контрактов блокчейна, требуя наличия подписи от конкретного аккаунта-участника бизнес-действия. Кроме этого, проверку подписей документов осуществляет бэкенд программного комплекса цифрового кооператива каждый раз при запросе документов пайщиком или членом совета.
Подпись документа¶
Как мы ранее могли обратить внимание, внизу каждого электронного документа на платформе есть блок подписи, давайте взглянем на него еще раз:

Мы видим контрольную сумму документа, а также блок с указанием количества подписей, где каждая подпись указывает на сертификат подписанта. Сертификаты выдаются пайщикам при регистрации и хранятся в базе данных кооператива. Мы извлекли сертификат пайщика на основании имени аккаунта подписанта, которое мы безопасно хранили в блокчейне.
Далее мы видим цифровую подпись. Как мы ранее говорили, она накладывается на контрольную сумму документа с помощью приватного ключа пайщика. А поскольку мы обладаем именем аккаунта пайщика, то мы всегда можем извлечь публичный ключ и произвести математическую сверку корректности подписи и ее принадлежности к указанному аккаунту. Что мы и делаем, когда показываем идентификатор статуса подписи - "Верифицирована".
Глубокая сверка¶
При необходимости из базы данных кооператива могут быть извлечены исходные данные, которые использовались для генерации полного документа, и использованы для того, чтобы на основании публичных мета-данных произвести глубокую сверку.
При глубокой сверке фабрика документов произведет полную регенерацию документа из исходных данных, заполнив ими шаблон электронного документа заново, и если все в порядке - то получит тот же самый результат в виде полностью идентичной контрольной суммы документа, который и был подписан. Глубокую сверку документов рекомендуется производить у всех документов пайщиков до принятия решений по ним.
Фабрика документов¶
Итак, что же такое фабрика документов?
Фабрика документов - это архитектурный паттерн, который позволяет производить одинаковым образом разные документы за счет использования шаблонов и общих стандартов генерации.
Фабрика предоставляет конвейеры по производству электронных документов, которые работают в целом одинаково: извлекают шаблон документа из блокчейна, подставляют данные пайщиков, формируют полный документ и возвращает его пайщику. Т.е. фабрика отвечает за генерацию и глубокую сверку документов, а также за резервное хранение полных документов и сертификатов пайщиков с их личными данными с полной историей изменений, на основании которых эти полные документы генерировались.
Таким образом, фабрика способна в любой момент предоставить или полный документ или произвести глубокую сверку документа на основании своих исторических данных.
Мы не будем в данном разделе подробно останавливаться на принципах генерации документов из шаблов, т.к. это продукт стандартов кооперативной экономики и данный материал будет описан на соответствующих страницах сайта https://coopenomics.world.
Реестр шаблонов¶
Для разработчиков¶
Генерация и подпись¶
Для того, чтобы сгенерировать документ, необходимо передать ему набор обязательных параметров из интерфейса IInput. Обязательные параметры являются частью процесса генерации документа, а не обязательные параметры обычно являются мета-данными документа.
Т.е. в результате генерации документа будет получен объект мета-данных, который можно использовать для глубокой сверки, передав их в качестве опциональных параметров.
import { Mutations } from '@coopenomics/sdk';
const variables: Mutations.Participants.GenerateParticipantApplication.IInput = {
data: {
block_num?: <null | number>; // Номер блока, на котором был создан документ
braname: <string>; // Имя аккаунта кооперативного участка
coopname: <string>; // Название кооператива, связанное с документом
created_at?: <null | string>; // Дата и время создания документа
generator?: <null | string>; // Имя генератора, использованного для создания документа
lang?: <null | string>; // Язык документа
links?: <null | string[]>; // Ссылки, связанные с документом
signature?: <null | string>; // Изображение собственноручной подписи (base-64)
skip_save: <boolean>; // Флаг пропуска сохранения документа (используется для предварительной генерации и демонстрации пользователю)
timezone?: <null | string>; // Часовой пояс, в котором был создан документ
title?: <null | string>; // Название документа
username: <string>; // Имя пользователя, создавшего документ
version?: <null | string>; // Версия генератора, использованного для создания документа
};
options?: {
lang?: <null | string>; // Язык документа
skip_save?: <null | boolean>; // Пропустить сохранение
};
};
const { [Mutations.Participants.GenerateParticipantApplication.name]: result } = await client.Mutation(
Mutations.Participants.GenerateParticipantApplication.mutation,
{ variables }
);
Результат
interface IOutput {
generateParticipantApplication: {
binary: <string>; // Бинарное содержимое документа (base64)
full_title: <string>; // Полное название документа
hash: <string>; // Хэш документа
html: <string>; // HTML содержимое документа
meta: <unknown>; // Метаданные документа
};
}
Используем полученный документ в result для создания подписи:
import { Mutations, Classes } from '@coopenomics/sdk'
// 2. Подписываем документ с помощью SDK-класса Document
const signer = new Classes.Document('WIF_PRIVATE_KEY')
const signed = await signer.signDocument(document, 'user1', 1) //1 - это порядковый номер подписи
Результат
interface SignedBlockchainDocument {
doc_hash: <string>; // Хэш содержимого документа
hash: <string>; // Общий хэш (doc_hash + meta_hash)
meta: <string>; // Метаинформация документа (в формате JSON-строки)
meta_hash: <string>; // Хэш мета-данных
signatures: <ModelTypes["SignatureInfo"][]>; // Вектор подписей
version: <string>; // Версия стандарта документа
}
Полученный signed документ может быть использован для отправки в мутацией согласно бизнес-процессам, которые мы далее рассматриваем по документации.
Важно
Приватный ключ (WIF) должен соответствовать имени пользователя (username), иначе процесс валидации подписи не будет завершен с успехом.
Мультиподпись¶
Когда необходимо создать дополнительную подпись на документе, то мы это делаем на основе подписанного документа, который уже опубликован в блокчейне. Мы берем его, и создаем еще одну подпись, накладывая её поверх ранее подписанного другим пайщиком документа, указывая порядковый номер подписи следующим.
const signed = await signer.signDocument(document, 'user2', 2)