Ethereum является действующим стандартом для смарт-контрактов общего назначения, но любой, кто занимался разработкой для Ethereum знает, что существуют серьезные проблемы, связанные с его программной архитектурой. На этой неделе я провел некоторое время, работая над подтверждением концепции новой системы тьюринг-полных смарт-контрактов, и в процессе установил некоторые существенные различия в философских подходах.
Технические проблемы Ethereum
Прежде чем вдаваться в детали того, что я узнал, давайте рассмотрим технические проблемы, с которыми сталкиваются разработчики смарт-контрактов на Ethereum.
1. Производительность
Анализ производительности в феврале 2016 года показал, что клиенту Ethereum Parity требуется более часа, чтобы обработать транзакции за 6 месяцев (1 миллион блоков). Все эти блоки были произведены до недавней компьютерной атаки на сеть Ethereum.
Для сравнения, обработка 6 миллионов блоков Steem со средней скоростью транзакций, значительно превышающей скорость транзакций Ethereum, может занять всего несколько минут. Скорость обработки данных Steem выше более чем в 20 раз.
Скорость, с которой один процессорный поток может обрабатывать виртуальную машину напрямую влияет на пропускную способность сети. Текущая сеть Ethereum поддерживает чуть больше 20 транзакций в секунду. Steem, в отличии от него, может поддерживать 1000 транзакций в секунду.
Недавняя атака на Ethereum смогла полностью перенасытить сеть и привела к отказу в обслуживании пользователей. Steem же с легкость пережил направленный на него флуд атаки без перерывов в обслуживании пользователей, и это без каких-либо комиссий за транзакцию!
Есть несколько причин почему ВМЭ — Виртуальная Машина Эфира (EVM - Ethereum Virtual Machine) медленная.
- Доступ к хранению основан на Level DB и 32 битных парах ключ/значение
- 256 битные операции очень медленны для обычных вычислений
- Подсчет потребления ГАЗа является частью консенсуса
- Планировка Внутренней Памяти Скрипта также является частью консенсуса (см. 1.)
- Мало возможностей по оптимизации скриптов EVM
Об утверждениях о Безграничной Масштабируемости
Виталик Бутерин заявляет, что Ethereum предложит “безграничную” масштабируемость в течение двух лет.
Это заявление основано на идее, что не всем узлам сети (node) нужно обрабатывать все транзакции. Это смелое заявление, как я думаю, не удастся осуществить на практике по причинам, которые я опишу ниже.
Их подход — “фрагментировать” (shard) блокчейн, что можно рассматривать как способ сделать блокчейн “многопоточным”. Каждый узел сети будет обрабатывать “один поток” и каждый “поток” сможет обработать 20 транзакций в секунду. В теории они смогут добавить неограниченное количество узлов в сеть и объем транзакций сможет масштабироваться неограниченно. Давайте предположим, что существует два полностью независимых смарт контракта. Эти два контракта могут работать на собственном ”фрагменте” со скоростью в 20 транзакций в секунду. Но что произойдет, если этим контрактам потребуется обмениваться информацией друг с другом? Решение будет в передаче сообщений от контракта к другому контракту. Любой, кто создавал мультипоточные программы с передачей сообщений знает, что это не стоит усилий, если затраты на обработку передачи сообщений достаточно низки.
Затраты на передачу сообщений между узлами через интернет очень высоки. “Цена” “прочтения” единичного значения из другого фрагмента будет значительной.
Наконец, разработчики многопоточных программ знакомы с идеей, что каждый поток “владеет” данными, которые он обрабатывает. Все, что хочет дотянуться до этих данных, должно пройти через их владельца. Что произойдет, когда один фрагмент владеет данными, к которым обращаются чаще 20 раз в секунду? В какой-то момент единичный поток создаст затор.
Если реализовать фрагментирование в Steem, это создаст заторы на уровне “глобального состояния”, на которое влияет каждый голос. То же самое произойдет с любой биржей, обрабатывающей лимитированные ордеры. Фрагментирование просто не масштабируется линейно, и уж точно не безгранично.
2. Ценообразование
Для того, чтобы исполнять программы на Ethereum, контракты должны платить ГАЗом. ВМЭ считает все исполненные инструкции, смотрит, сколько газа стоит эта инструкция, и списывает за нее средства. Если у контракта заканчивается ГАЗ, то все изменения, сделанные контрактом, откатываются, но производитель блока все равно оставляет оплату за ГАЗ.
Попытка реализации чего-то подобного Steem на Ethereum’е сталкивается с тем, что пользователям придется платить $0.01 за каждый голос и даже больше за пост, что является большой проблемой. С ростом числа пользователей сеть переполнится и цена ГАЗа возрастёт.
Теперь давайте представим, что Steem не единственное приложение на Ethereum’е, представим, что Голос и Augur тоже стали популярны, с миллионом пользователей каждый. Цена ГАЗа будет расти до тех пор, пока не остановит развитие всех трех приложений.
Единственный способ снизить цены — увеличить пропускную способность транзакций, повысив эффективность.
Улучшение эффективности - неравномерный процесс. Установка дисков с более высокой скоростью не увеличит эффективность вычислений. Увеличение скорости вычислений не поможет со скоростью обращения к диску. Все попытки увеличения эффективности обязательно повлияют на относительную цену каждой операции в ГАЗе.
Недавно Ethereum был вынужден провести хардфорк для изменения расходования газа. В прошлый раз, когда у Ethereum был хардфорк, это привело к появлению Ethereum Classic!
Можно с уверенностью говорить, что все попытки оптимизации ВМЭ будут изменять относительную стоимость операций. Цена ГАЗа может быть снижена на пропорциональную инструкции величину, которая будет наименее оптимизирована.
И хотя оптимизация некоторых инструкций может увеличить размер прибыли валидаторов блоков, разработчикам Смарт Контрактов все равно придется платить завышенные цены.
Поскольку ГАЗ - это часть консенсуса, всем узлам нужно продолжать обрабатывать старые блоки, используя старые подсчеты ГАЗа до тех пор, пока не произойдет хардфорк. Это означает, что будущие оптимизации сдерживаются необходимостью поддерживать оригинальный метод учета.
3. Оптимизация кода
Одним из главных источников оптимизации является не улучшение аппаратного обеспечения вашего компьютера, а улучшение ПО. В частности, компиляторы могут творить чудеса в улучшении производительности кода, запускаемого на том же компьютере.
У компиляторов есть возможность оптимизировать, потому что у них есть доступ к информации о задумке программиста. Как только код конвертируется в машинный язык, теряется множество возможностей по оптимизации.
Представьте, что кто-то решил оптимизировать целый контракт, предоставив нативную имплементацию. Нативная имплементация будет давать одинаковые результаты на выходе при одинаковых данных на входе, но она не будет знать как считать цены на ГАЗ, потому что она работала не на ВМЭ.
4. Задумка программиста
Смарт контракты Ethereum публикуются в виде скомпилированного байт-кода, который обрабатывается интерпретатором. Для того, чтобы люди могли понять смарт контракт, им нужно читать код, но блокчейн хранит не исходный код, а ассемблерный. Людям приходится проверять, выдаёт ли “скомпилированный код” те данные, которые ожидаются от исходного кода.
У такого подхода есть несколько проблем. Он требует, чтобы все разработчики компиляторов генерировали одинаковый код и использовали одинаковые оптимизации, или чтобы все контракты проверялись через выбранный компилятор.
В любом случае, скомпилированный код на шаг отдаляется от высказанных задумок создателей контрактов. Ошибки компилятора становятся нарушением задумок программиста, и эти ошибки невозможно исправить изменением консенсусной интерпретации, потому что исходный код не известен консенсусу.
Другой подход
Создатели языка Си++ следуют философии, что нужно определять ожидаемое поведение какого-то кода без определения как именно следует достигать подобного поведения. Это означает, что разные компиляторы могут генерировать различный код с разным распределением памяти на разных платформах.
Это также означает, что разработчики могут сконцентрироваться на том, что они хотят выразить и могут получить результаты, которых они ожидают, без наложения ненужных ограничений на разработчиков компиляторов, или на аппаратное обеспечение. Это максимально увеличивает возможность оптимизировать производительность при этом соответствуя спецификации.
Представьте себе платформу для смарт контрактов, где разработчики публикуют код, который они хотят запустить, а консенсус блокчейна обязан правильно интерпретировать код, но не имеет обязательств в том как выполнять этот код.
В этом примере можно заменить скрипт на предварительно скомпилированный бинарный код, используя другой алгоритм, и все будет окей до тех пор, пока вводимые и исходящие из черного ящика данные остаются теми же самыми. С Etheruem это невозможно, потому что чёрному ящику требуется считать сколько всего истрачено ГАЗа.
Лучшее решение проблемы ГАЗа
ГАЗ - это грубый подход к подсчету детерминистического времени исполнения программ. В идеальном мире мы бы просто использовали бы настенные часы, но компьютеры с разными характеристиками и нагрузками получат разные результаты. Хотя и невозможно прийти к детерминистическому консенсусу о том, сколько времени что-то занимает, вполне возможно достигнуть консенсуса о том, включать ли трансляцию [в блок - прим. пер.] или нет. Представьте, как несколько людей в комнате пытаются решить - включать ли какую-то транзакцию или нет. Каждый из них измеряет время, необходимое для обработки транзакции, по настенным часам, и используют вытесняющее планирование для остановки исполнения, если оно происходит слишком долго.
После измерений они все голосуют, и если большинство говорит, что все было “ок”, тогда все включают эту транзакцию. Сеть не знает “сколько это заняло времени”, она только знает, что транзакция выполнена за одобренное время. И отдельные компьютеры будут выполнять транзакции вне зависимости от того, сколько это займет, как только они будут знать, что консенсус достигнут.
С точки зрения консенсуса это означает, что все скрипты платят одинаковую цену вне зависимости от проведенных вычислений. Скрипты платят за “промежутки времени фиксированной длины”, вместо того, чтобы платить за “вычисления”. В терминах, понятных разработчикам операционных систем — скрипты должны выполняться в выделенный им на это квант времени, или их вытеснят и работа будет потеряна.
Вышеописанный подход очень абстрактен и не будет практичным в реализации с прямым голосованием, но есть способ реализовать его, который масштабируется без больших накладных расходов, чем уже существуют в Steem. Для начала, все производители блоков должны произвести свой блок в сжатые сроки. Если они не успевают в отведенное время, то это делает следующий делегат. Это означает, что производители блоков должны добавить свой блок и распространить его в сети, достигнув большинства узлов (включая следующего делегата), до времени следующего блока.
Это означает, что просто присутствие транзакции в блоке — знак, что сеть смогла обработать блок и все присутствующие в нем транзакции вовремя. Каждый узел сети может “голосовать” о том, как долго обрабатывался блок и его транзакции. Таким образом, узлу не нужно пересылать блок, если ему кажется, что транзакции превышают выделенное им время.
При этом узел, который против передачи блока из-за большого времени на исполнение, все равно будет принимать новые блоки, следующие за этим “плохим” блоком. В какой-то момент узел встретит ответвление цепочки большей длины и переключится на нее, или “плохой” блок получит достаточно подтверждений (голосов) и станет необратимым. Как только это произойдет, узел начнет передавать его и все, что за ним следует.
Производитель блоков, который хочет получать оплату, будет пытаться сделать так, чтобы его блоки распространялись и поэтому будет “консервативен” в предположениях о замерах с настенных часов, которые производят другие узлы. Сети нужно будет изменять награды за блоки в пропорции к количеству включенных транзакций.
Из-за естественного “ограничения скорости”, связанного с пропускной способностью / квантом на каждую вложенную долю (vesting stake), потребуется серьезное вложение, чтобы какой-либо один майнер смог бы наполнить свой блок, просто чтобы получить бонус.
Предотвращение ДоС атак (Предотвращение Отказов в Обслуживании)
Одна из проблем со скриптами в том, что атакующему ничего не стоит создать бесконечный цикл. Проверяющим узлам приходится тратить ресурсы, даже если конечным решением будет отклонить скрипт. И в этом случае проверяющему не платят за потраченные ресурсы.
Чтобы справиться с такого рода злоупотреблениями, проверяющие могут выбрать один из двух путей:
- Создать локальные черные и белые списки скриптов, учетных записей и пиров (peer)
- Требовать доказательства выполнения работы (proof-of-work) от каждого скрипта
Использование доказательства выполнения работы позволяет проверяющему узнать, что создатель скрипта затратил минимальные усилия. Чем больше выполнено работы, тем больше времени по “настенным часам” проверяющий выделит на выполнение скрипта до какого-то максимального предела.
Тому, кто желает распространить транзакцию, которая вычислительно затратна, придется создать более сложное доказательство выполнения работы, чем тому, кто создает менее дорогую транзакцию.
Это доказательство выполнения работы совмещенное с ТрДоД (TaPoS Transactions as Proof of Stake) Транзакции как Доказательство Доли означает, что мы теперь имеем Транзакции как Доказательство Выполнения Работы, которые коллективно обеспечивают безопасность сети.
У этого подхода есть побочный эффект в том, что он не дает делегатам “набить собственный блок” чтобы получить вознаграждение, потому что каждая созданная ими транзакция потребует доказательства выполненной работы. Таким образом, блокчейн будет вознаграждать делегатов за транзакции на основании сложности доказательства выполненной работы, как объективного мерила сложности выполнения скрипта.
Подтверждение работоспособной концепции
Недавно я написал немного кода на скриптовом языке Wren со встроенными в него экспериментальными блокчейн операциями. Вот как выполнить простую “крипто валюту” в один смарт контракт:
test_script = R"(
class SimpleCoin {
static transfer( from, to, amount ) {
var from_balance = 0
var to_balance = 0
if( from != Db.current_account_authority().toString )
Fiber.abort( "invalid authority" )
var a = Num.fromString( amount )
if( a < 0 ) Fiber.abort( "cannot transfer negative balance" )
if( Db.has( from ) )
from_balance = Num.fromString(Db.fetch( from ))
if( Db.has( to ) )
to_balance = Num.fromString(Db.fetch( to ))
if( from_balance <= 0 && Db.script_account().toString != from)
Fiber.abort( "insufficient balance" )
from_balance = from_balance - a
to_balance = to_balance + a
Db.store( from, from_balance.toString )
Db.store( to, to_balance.toString )
}
}
)";
trx.operations.emplace_back( set_script{ 0, test_script } );
trx.operations.emplace_back(
call_script{
account_authority_level{1,1}, 0,
"SimpleCoin",
"transfer(_,_,_)", {"1","0","33"}
}
);
Я ввёл две операции на уровне блокчейна: set_script, и call_script. Первая операция назначает скрипт учетной записи (учетная запись 0), а вторая операция вызывает метод, определенный в скрипте.
Скриптовая среда имеет доступ к состоянию блокчейна при помощи Db
API базы данных. Из этого API он может загружать и хранить данные, характерные для этого скрипта, а также запрашивать информацию о текущем уровне авторизации операции. Операция call_script
объявляет, что “учетная запись 1” с уровнем авторизации “1”, т.н. активная авторизация, подтвердила вызов. И тогда она запустит скрипт на “учетной записи 0” и вызовет SimpleCoin.transfer( “1”, “0”, “33” )
.
Метод передачи может подтвердить, что current_account_authority
совпадает с полем from
вызова передачи.
Тестирование работоспособной концепции
Я запустил симуляцию, обрабатывающую тысячи транзакций, каждая из которых содержит один вызов функции ‘SimpleCoin.transfer’ и замерил время, потребовашееся на выполнение. В общем мой компьютер смог произвести более 1100 транзакций в секунду через интерпретатор. Это уровень производительности до каких-либо оптимизаций и/или кэширования скрипта. Другими словами, измеренные 1100 транзакций в секунду включают в себя компилирование скрипта 1100 раз. Более разумная имплементация кешировала бы скомпилированный код для существенных улучшений в скорости.
Для сравнения, если мы предположим, что “Безграничный” Ethereum идеально масштабировался, потребовалось бы 55 узлов для обработки этих же транзакций, которые только что обработал мой единственный узел. К тому моменту, когда Wren будет оптимизирован с нормальным кешированием, а Ethereum сделает скидку на необходимые дополнительные издержки, платформа для смарт контрактов на базе технологий Steem и Wren сможет быть в сотни раз более эффективной, чем Ethereum.
Зачем тьюринг-полные смарт-контракты?
Децентрализованное управление отчасти зависит от децентрализованного исполнения контрактов. Блокчейн не может наперёд знать, что каждый контракт будет полезен, и политически централизующая необходимость хардфорков для поддержания новых смарт контрактов ограничивает возможность органично прийти к пониманию, что же работает.
С подходящим движком для смарт контрактов разработчики смогут экспериментировать с “низкопроизводительными скриптами” и потом заменять свои “медленные скрипты” более высокопроизводительными версиями, которые соответствуют тому же интерфейсу черного ящика. Это позволит избавиться от необходимости детерминистически подсчитывать ГАЗ и распределение памяти, что является абсолютно критичным для воплощения желаемых улучшений производительности.
Вывод
Технология, лежащая в основе Steem, в сочетании с описанной реализацией смарт-контрактов будет в сотни раз более масштабируемой, чем технология Ethereum, полностью исключая операционные сборы за выполнение смарт-контрактов. Это может открыть двери тысячам новых приложений, которые не являлись бы экономически целесообразными на Ethereum.
Оригинальный пост и его обсуждение ЗДЕСЬ
Перевод осуществлен: @xanoxt
Критика, комментарии и предложения приветствуются.
Умные контракты на WREN - ок! ждем, это будет круто! Бутерик не потянет ;)
Всё стим похоронит эфир? По цене не скажешь :)
Наконец то! мы уже заждались...теперь РУ-читателей в Голосе больше чем в Стиме, так что мост"РуСтимитБлог" должен стать связующим звеном между площадками.
P.S. тема со смарт контрактами крайне важная, если тьюринг-полные смарт контракты будут реализованы на архитектуре Graphene это действительно будет прорыв, Стим и Голос смогут выйти на принципиально новый уровень.
Свершилось! Наконец-то Рустимблог появился на Голосе)
Кто бы сомневался, новые боты захламляют Голос ))