📒 EOS.IO Структура транзакции - Журнал разработчика. Звездная дата 201707.9 (@dan)
Сегодня мне хотелось бы уделить время объяснению текущей структуры транзакции в EOS.IO, чтобы разработчики лучше понимали модель параллелизма. Далее приведена отображенная в формате JSON транзакция, которая перечисляет валюту от сэма к алисе. В данном случае валюта, сэм и алиса - это всё имена аккаунтов, но используются они по-разному.
{
"refBlockNum": "12",
"refBlockPrefix": "2792049106",
"expiration": "2015-05-15T14:29:01",
"scope": [
"alice",
"sam"
],
"messages": [
{
"code": "currency",
"type": "transfer",
"recipients": [
"sam",
"alice"
],
"authorization": [
{
"account": "sam",
"permission": "active"
}
],
"data": "a34a59dcc8000000c9251a0000000000501a00000000000008454f53000000000568656c6c6f"
}
],
"signatures": []
}
Эта транзакция, будучи сериализованной\преобразованной в двоичный код и подписанной единственной подписью, имеет длину около 160 байтов, что немного больше, чем операция в Steem, которая занимает около 120 байтов, или трансфер BitShares, который составляет 94 байта. Увеличение размера вызвано необходимостью обязательно указывать реципиентов, авторизацию и область действия, что, в общей сложности, добавляет 51 байт к сообщению.
TaPoS - транзакции как доказательство участия
Те, кто уже знаком со Steem и BitShares, поймут, что обозначают первые три поля в транзакции. Они остались без изменений и используются для TaPoS (Транзакции как доказательства участия), чтобы гарантировать что данная транзакция может быть включена в блок после указанного блока и до истечения срока годности/экспирации.
Область действия
Следующее поле, "область действия" является нововведением в EOS.IO и указывает на диапазон данных, откуда можно считывать и/или куда можно записывать данные. Если сообщение попытается прочитать или записать данные, выходящие за область действия, то транзакция не пройдет. Транзакции можно обрабатывать параллельно, если их области действия не пересекаются.
Ключевое нововведение в EOS.IO заключается в том, что область действия и код - две полностью разделенные концепции. Вы заметите, что контракт валюта не указан в области действия, несмотря на то, что выполняет перевод, используя код валютного контракта.
Сообщения
Транзакция может состоять из одного или нескольких сообщений, которые должны быть применены в порядке отправления и единообразно (все успешно или все неудачно). В данном примере у нас есть одно единственное сообщение. Давайте рассмотрим его детально.
code:
Каждое сообщение должно указывать, какой код оно будет исполнять. В данном случае будет исполняться код валютного контракта, который приведет к вызову следующего метода:
currency::apply_currency_transfer(data)
type:
Поле "тип" задает тип сообщения и подразумевает формат данных. В парадигме объектно-ориентированного программирования мы можем рассматривать тип как метод"имя" принадлежащий классу"валюта". В нашем примере тип это "перечислить", что объясняет наименование вызываемого метода:
${namespace}::apply_${code}_${type}( data )
В нашем случае "пространство имён" - это контракт валюта, однако этот же метод apply_currency_transfer
может быть вызван и в других пространствах имён.
recipients:
Для каждого перечисленного получателя\реципиента будет вызван метод apply_currency_transfer(data)
, в дополнение к методу currency::apply_currency_transfer(data)
. Например, следующие методы будут последовательно вызваны в следующем порядке:
currency::apply_currency_transfer(data)
alice::apply_currency_transfer(data)
sam::apply_currency_transfer(data)
Запись account::
указывает на контракт, который реализует метод. алиса и сэм могут не реализовывать этот метод, если они не располагают особой логикой, которую нужно выполнять, когда исполнятся currency::apply_currency_transfer
. Однако, если бы сэм был был обменным пунктов, то тогда сэму, скорее всего, пришлось обрабатывать начисления и списания валюты каждый раз, когда осуществлялась бы передача валюты.
Лицо, которое формирует транзакцию, может включить в нее любое количество получателей (при условии, что все они будут достаточно быстро обработаны). Кроме того, некоторые контракты могут требовать отправки извещений определенным сторонам контракта. В случае валютного контракта, требуется извещение как отправителя, так и получателя. Вы можете посмотреть, как это указано в валютном контракте
void apply_currency_transfer() {
const auto& transfer = currentMessage<Transfer>();
requireNotice( transfer.to, transfer.from );
...
}
authorization:
Каждое сообщение может требовать авторизации\разрешений от одного или нескольких аккаунтов. В Steem и BitShares необходимые авторизации неявно определялись на основании типа сообщения, однако в сообщениях EOS.IO нужно явно определять, какие разрешения необходимы. Система EOS.IO автоматически проверит, было ли сообщение подписано всеми нужными подписями, которые дают необходимые разрешения.
В данном случае сообщение указывает, что оно должно быть подписано уровнем активных разрешений сэма. Код валютного контракта проверит, что авторизация сэма была предоставлена. Вы можете увидеть эту проверку в примере валютного контракта.
void apply_currency_transfer() {
const auto& transfer = currentMessage<Transfer>();
requireNotice( transfer.to, transfer.from );
requireAuth( transfer.from );
...
}
data:
Любой контракт может определять собственный формат данных. Без бинарного интерфейса приложений данные могут быть обработаны как шестнадцатеричные числа. Однако контракт валюта определяет формат данных как структуру Перечислить:
struct Transfer {
AccountName from;
AccountName to;
uint64_t amount = 0;
};
Вооружившись этим определением мы можем преобразовать двоичный блоб в нечто, похожее на:
{ "from" : "sam", "to": "alice", "amount": 100 }
Порядок выполнения
Теперь, когда мы понимаем структуру транзакций EOS.IO, можно изучить структуру блока EOS.IO. Каждый блок поделен на циклы, которые исполняются последовательно. В каждом цикле есть несколько потоков, которые исполняются параллельно. Хитрость заключается в том, чтобы гарантировать, что ни одна пара потоков не содержит транзакции с пересекающимися областями действия. Блок может быть объявлен недействительным безотносительно каких-либо внешних данных при условии, если существует пересечение областей действия между потоками в одном цикле.
Заключение
Самая большая сложность при параллельном вычислении - гарантироваться, что два потока не будут обращаться к одним и тем же данным одновременно. В отличие от традиционного параллельного программирования, мы не можем использовать блокировки доступа к памяти, так как выполнение, в таком случае, будет недетерминированным и может потенциально нарушить консенсус. Даже если бы блокировки были возможны, их использование было бы нежелательным, так как их активное применение сделало бы производительность меньше, чем при вычислениях в один поток.
Альтернативой блокировки данных по времени доступа является блокировка по времени выполнения. При таком подходе область действия определяет аккаунты, доступ к которым блокирует транзакция. Планировщик выполнения (он же производитель блоков) гарантирует, что ни одна пара потоков не попытается заблокировать те же данные в одно и то же время. При помощи такой структуры транзакций и метода планирования вычислений можно существенно уменьшить конфликты при обращении к памяти и предоставить возможность параллельного исполнения.
Отделение кода (валютного контракта) от данных (хранилища контракта) позволяет разграничить требования по блокировке памяти. Если бы валютный контракт и его данные были бы объединены, то тогда каждое перечисление средств требовало бы блокировки валютного контракта и все перечисления были бы ограничены по скорости обработки единственным потоком. Но так как сообщение о переводе средств блокирует только данные отправителя и получателя, сам валютный контракт больше не является узким местом.
Вывод валютного контракта из положения узкого места - чрезвычайно важное достижение. Рассмотрим контракт по обмену валют. При каждом начислении или снятии средств со счета обменника, валютный контракт и контракт обменника будут обрабатываться в одном и том же потоке. Если оба эти контракта активно используются, то тогда это негативно повлияет на производительность операций со всеми валютами для всех пользователей обменника.
В модели EOS.IO два пользователя могут пересылать средства, не беспокоясь о последовательном быстродействии (т.е. в рамках одного потока) других аккаунтов\контрактов, кроме тех, которые непосредственно задействованы в транзакции.
Отказ от ответственности: В этом посте нет ничего, что следует рассматривать как предлагаемую конкретную функциональную возможность или алгоритм работы программного обеспечения EOS.IO. Все проектные решения разработки программного обеспечения могут быть изменены по мере необходимости.
Оригинал поста: ЗДЕСЬ