Контекст транзакции¶
Аудитория: Архитекторы, разработчики смарт-контрактов и приложений.
Контекст транзакции исполняет две функции. Во-первых, он позволяет разработчику определять и поддерживать пользовательские переменные во время вызовов транзакций в рамках смарт-контракта. Во-вторых, он предоставляет доступ к широкому спектру API Fabric, которые позволяют разработчикам смарт-контрактов выполнять операции, связанные с детальной обработкой транзакций: от запросов или обновления реестра, как неизменяемого блокчейна, так и изменяемого состояния, до получения цифрового идентификатора приложения, отправляющего транзакцию.
Контекст транзакции создается, когда смарт-контракт развертывается в канале и становится доступным для каждого последующего вызова в транзакции. Контекст транзакции позволяет разработчикам смарт-контрактов писать функциональные, эффективные и легкие для понимания программы.
- Почему контекст транзакции важен
- Как пользоваться контекстом транзакции
- Что он содержит контекст транзакции
stub
в контексте транзакцииclientIdentity
в контексте транзакции
Сценарий¶
В примере коммерческих ценных бумаг, контракт papercontract изначально задает имя списка коммерческих ценных бумаг, за которые отвечает. Каждая транзакция впоследствии ссылается на этот список; транзакция выпуска добавляет в него новые бумаги, транзакция покупки меняет владельца бумаги, а транзакция погашения помечает ее как завершенную. Это общий шаблон; при написании смарт-контракта часто полезно инициализировать переменные и обращаться к ним в последовательных транзакциях.
Контекст транзакции позволяет смарт-контрактам задавать и поддерживать пользовательские переменные в различных вызовах транзакций. Подробное пояснение см. в тексте.
Программирование¶
При конструировании смарт-контракта разработчик может по желанию переопределить метод createContext
встроенного класса Context
, чтобы создать собственный контекст:
createContext() {
new CommercialPaperContext();
}
В нашем примере контекст CommercialPaperContext
определен для контракта CommercialPaperContract
.
Посмотрите, как пользовательский контекст, адресованный через this
,
добавляет в себя определенную переменную PaperList
:
CommercialPaperContext extends Context {
constructor () {
this.paperList = new PaperList(this);
}
}
Когда метод createContext() возвращается в точку (1) на диаграмме
выше, создается пользовательский контекст ctx
, содержащий
paperList
как одну из своих переменных.
Впоследствии, когда бы ни вызывалась транзакция смарт-контракта, будь то выпуск, покупка
или погашение, ей будет передан этот контекст. Ниже покажем, как в точках (2), (3)
и (4) тот же контекст коммерческой ценной бумаги передается в метод при помощи
переменной ctx
.
Посмотрите, как контекст используется в точке (5):
ctx.paperList.addPaper(...);
ctx.stub.putState(...);
Обратите внимание, что paperList
, созданный в CommercialPaperContext
, доступен в
транзакции выпуска. Аналогичным образом paperList
используется
и для транзакций погашения и покупки; ctx
таким образом делает смарт-контракты
эффективными и понятными.
Также обратите внимание, что в контексте есть и другой элемент – ctx.stub
–
который не был явным образом добавлен CommercialPaperContext
. Это оттого, что
stub
и прочие переменные являются частью встроенного контекста. Давайте теперь
изучим структуру этого встроенного контекста, эти неявные переменные и как их использовать.
Структура¶
Как мы видели в примере, контекст транзакции может
содержать сколько угодно пользовательских переменных, таких как paperList
.
Контекст транзакции также содержит два встроенных элемента, которые предоставляют доступ к широкому спектру функциональности Fabric – от клиентского приложения, посылающего транзакцию, до доступа к реестру.
ctx.stub
используется для доступа к API, которые предоставляют широкий диапазон операций обработки транзакций, отputState()
иgetState()
для доступа к реестру, доgetTxID()
для извлечения идентификатора текущей транзакции.ctx.clientIdentity
используется для получения информацию об идентификаторе пользователя, пославшего транзакцию.
На следующей диаграмме показано, что может сделать смарт-контракт, используя stub
and clientIdentity
,
с помощью доступных ему API:
Смарт-контракт может получить доступ к ряду
возможностей через stub
и clientIdentity
из контекста транзакции. Подробное пояснение - далее в тексте.
Stub¶
Функционал stub
можно поделить на несколько категорий:
API базы состояния. См. точку взаимодействия (1). Эти функции дают смарт-контрактам возможность получать, записывать и удалять состояния, соответствующее отдельным объектам в глобальном состоянии, используя их ключ:
Эти основные функции дополняются запросами, при помощи которых контракты могут получать набор состояний, а не только отдельные состояния. См. точку взаимодействия (2). Набор можно задавать или диапазоном значений ключей, полных или частичных, или запросом в соответствии со значениями соответствующей базы данных состояний.
При больших запросах, результат можно делить на страницы из соображений уменьшения занимаемого места:API приватных данных. См. точку взаимодействия (3). При помощи этих функций смарт-контракты взаимодействуют с коллекциями приватных данных. Они аналогичны API для взаимодействия с глобальными состояниями и позволяют получать, записывать и удалять приватные данные, обращаясь к ним по их ключу:
Эти основные функции дополняются запросами приватных данных (4). С их помощью смарт-контракты извлекают набор состояний из коллекций приватных данных по диапазону значений ключей (полных или частичных) или запросом в соответствии со значениями соответствующей базы данных состояния. В настоящее время нет возможности разделения на страницы для коллекций приватных данных.API транзакций. См. точку взаимодействия (5). Этими функциями смарт-контракты извлекают подробные данные о предложении транзакции, обрабатываемом смарт-контрактом в настоящее время. Они включают и идентификатор транзакции и время создания предложения транзакции.
- getTxID() возвращает идентификатор предложения транзакции (5).
- getTxTimestamp() возвращает метку времени создания приложением предложения транзакции (5).
- getCreator()
возвращает необработанный идентификатор (X.509 или др.) создателя предложения
транзакции. Если это сертификат X.509, тогда бывает удобнее использовать
ctx.ClientIdentity
. - getSignedProposal() возвращает подписанную копию текущего предложения транзакции, находящегося в обработке у смарт-контракта.
- getBinding() предотвращает случайный или злонамеренный повторный вызов транзакции при помощи одноразового кода (nonce). (На практике, одноразовый код - это случайно сгенерированное клиентским приложением число, включенное в криптографический хэш). К примеру, эта функция может использоваться смарт-контрактом в точке (1), чтобы обнаружить повторный вызов транзакции (5).
- getTransient() позволяет смарт-контракту обращаться к транзитным данным, переданным приложением. См. точки взаимодействия (9) и (10). Транзитные данные являются приватными данными взаимодействия «приложение-смарт-контракт». Они не записываются в реестр и часто используются вместе с коллекциями приватных данных (3).
API ключей используется смарт-контрактами для операций с ключами состояния в глобальном состоянии или коллекции приватных данных. См. точки взаимодействия 2 и 4.
Простейшие из функций этого API позволяют смарт-контрактам формировать и разделять композитные ключи на их отдельные компоненты. Немного более продвинутыми являются API
ValidationParameter()
, которые получают и устанавливают основанные на состоянии правила одобрения для глобального состояния (2) и приватных данных (4). И, наконец,getHistoryForKey()
получает историю состояния, возвращая набор хранимых значений, включающих идентификаторы транзакции, с помощью которых было проведено изменение состояния, что позволяет прочитать транзакцию из блокчейна (10).- createCompositeKey()
- splitCompositeKey()
- setStateValidationParameter()
- getStateValidationParameter()
- getPrivateDataValidationParameter()
- setPrivateDataValidationParameter()
- getHistoryForKey()
API событий используются для работы с событиями в смарт-контракте.
-
Смарт-контракты при помощи этого API добавляют пользовательские события в ответ транзакции. См. точку взаимодействия (5). Эти события в итоге записываются в блокчейн и посылаются в ожидающие приложения в точке взаимодействия (11).
-
Вспомогательные API – это набор полезных API, которые не поместились в другие категории. С их помощью можно, в частности, получать имя текущего канала и передавать управление другому чейнкоду на одном и том же узле.
-
См. точку взаимодействия (13). Смарт-контракт, запущенный на любом одноранговом узле может при помощи этого API определить, на каком канале данное приложение вызвало смарт-контракт.
-
См. точку взаимодействия (14). На Peer3 (одноранговом узле 3), принадлежащем MagnetoCorp, установлено несколько смарт-контрактов. Эти смарт-контракты способны вызывать друг друга с помощью этого API. Смарт-контракты должны быть расположены в одном и том же узле, вызвать смарт-контракт с другого узла не получится.
Некоторые из вспомогательных API используются только в низкоуровневом чейкоде, а не в смарт-контрактах. Эти API предназначены прежде всего для операций с вводными данными чейнкода; классContract
смарт-контракта делает автоматический разбор этих параметров для разработчиков.-
ClientIdentity¶
В большинстве случаев, приложение, которое посылает транзакцию, использует
сертификат X.509. В нашем примере, сертификат X.509 (6), выпущенный
УЦ CA1
(7), используется пользователем Isabella
(8) в ее приложении для
подписывания транзакции t6
(5).
ClientIdentity
принимает информацию, которую возвращает ему getCreator()
, и помещает
поверх ее набор вспомогательных API X.509, чтобы облегчить ее использование в данном
часто встречающемся сценарии.
- getX509Certificate() возвращает полный сертификат X.509 того, кто послал транзакцию, включая все его атрибуты и их значения. См. точку взаимодействия (6).
- getAttributeValue()
возвращает значение конкретного атрибута X.509, например, название организационной единицы
OU
или выделенное имяDN
. См. точку взаимодействия (6). - assertAttributeValue()
возвращает
TRUE
, если определенный атрибут X.509 имеет заданное значение. См. точку взаимодействия (6). - getID()
возвращает уникальный идентификатор того, кто послал транзакцию в соответствии с его
выделенным именем и выделенным именем выпускающего УЦ в формате
x509::{subject DN}::{issuer DN}
. См. точку взаимодействия (6). - getMSPID() возвращает MSP канала того, кто послал транзакцию. Это позволяет смарт-контракту принимать решения об обработке решений на основе идентификатора организации пославшего. См. точку взаимодействия (15) or (16).