📒 Структура транзакций 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 (Transactions as Proof of Stake) и гарантируют, что эта транзакция может быть включена только после указанного блока и до экспирации.
Область видимости (переменных)
Следующее поле, “область видимости (переменных)” («scope»), является новым для EOS.IO и задает диапазон данных, которые могут быть прочитаны и/или записаны. Если сообщение попытается прочитать или записать данные за пределами области применения, транзакция завершится ошибкой. Транзакции могут обрабатываться параллельно, если области их применения не перекрывают друг друга.
Ключевым новшеством программного обеспечения EOS.IO является то, что область видимости (переменных) и код являются двумя совершенно отдельными концепциями. Вы заметите, что валютный контракт не указан в области видимости (переменных), даже если мы выполняем передачу, используя код валютного контракта.
Сообщения
Транзакция может содержать одно или несколько сообщений, которые должны применяться по порядку и атомарно (все успешны или все отменяются). В приведенном случае есть только одно сообщение, поэтому давайте рассмотрим его подробнее:
code:
Каждое сообщение должно указывать, какой код оно будет выполнять, в данном случае будет выполнен код валютного контракта, в результате чего вызывается следующий метод:
currency::apply_currency_transfer(data)
type:
Поле type определяет тип сообщения (и неявно формат данных). С точки зрения объектно-ориентированного программирования вы можете рассматривать тип как «имя» метода в классе «валюта». В этом примере тип - «transfer», что, следовательно, объясняет именование вызываемого метода:
${namespace}::apply_${code}_${type}( data )
В этом случае "namespace" является валютным контрактом; однако этот же метод apply_currency_transfer
также может быть вызван в других пространствах имен.
recipients:
В дополнение к вызову currency::apply_currency_transfer(data)
, метод 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:
Каждый контракт может задавать собственный формат данных. Без ABI данные могут быть интерпретированы только как шестнадцатеричные; однако валютный контракт определяет формат данных как Transfer Struct:
struct Transfer {
AccountName from;
AccountName to;
uint64_t amount = 0;
};
Руководствуясь этим определением мы можем преобразовать двоичный blob в нечто подобное:
{ "from" : "sam", "to": "alice", "amount": 100 }
Планировщик
Теперь, когда мы понимаем структуру транзакции EOS.IO, мы можем рассмотреть структуру блока EOS.IO. Каждый блок разделяется на циклы, которые выполняются последовательно. В каждом цикле имеется какое угодно количество потоков, выполняемых параллельно. Хитрость заключается в том, чтобы гарантировать, что никакие два потока не содержат транзакций с пересекающимися областями видимости (переменных). Блок может быть объявлен недействительным без ссылки на любые внешние данные, если между потоками в рамках одного цикла существует перекрытие областей видимости (переменных).
Заключение
Самой большой проблемой в параллельном выполнении является обеспечение того, что два потока не получают доступ к одним и тем же данным одновременно. В отличие от традиционного параллельного программирования здесь невозможно использовать блокировки доступа к памяти, поскольку результирующее выполнение обязательно будет недетерминированным и потенциально нарушит консенсус. Даже если бы блокировки были возможны, они были бы нежелательны, потому что их интенсивное использование может ухудшить производительность до того уровня, что она будет ниже производительности однопоточного исполнения.
Альтернативой блокировке во время доступа к данным является блокировка во время запланированного выполнения. Тогда поле области применения задает аккаунты, на которые транзакция хочет поставить блокировку. Планировщик (т.е. производитель блоков) гарантирует, что два потока не попытаются одновременно обратиться к одной и той же блокировке. Благодаря этой структуре транзакций и планированию графика можно резко снизить конфликтность доступа к памяти и увеличить возможности параллельного выполнения.
В отличие от методов других платформ разделение кода (валютного контракта) и данных (хранилища аккаунта) позволяет разделять требования к блокировке. Если бы валютный контракт и его данные были объединены, то каждый перевод должен был бы заблокировать валютный контракт, и все переводы бы стали ограничены однопоточной пропускной способностью. Но поскольку сообщение о переводе блокирует только данные отправителя и получателя, валютный контракт больше не является узким местом.
Устранение узкого места в виде валютного контракта невероятно важно, если рассматривать биржевой контракт. Каждый раз, когда на бирже производится депозит или вывод средств, валютный контракт и биржевой контракт попадают в один и тот же поток. Если оба этих контракта активно используются, это ухудшит производительность всей валюты и всех клиентов биржи.
В модели EOS.IO два пользователя могут передавать средства, не беспокоясь о последовательной (однопоточной) пропускной способности каких-либо аккаунтов/контрактов кроме тех, что участвуют в передаче.
Свежие новости в Телеграм: t.me/EOS_RU
Оригинал поста: ЗДЕСЬ