Юботы

Юботами мы называем смарт-контракты Universa, содержащие выполняемый код/скрипты особого вида. Такой код может запускаться в сети узлов распределенного исполнения, работающей под контролем и в симбиозе с сетью Universa. Используется современный вариант JavaScript/ECMAScript (async/await, optional chaining, null coalesce, etc), работающий в уникальной распределенной среде.

В результате получается продукт с уникальным набором харакетистик, среди которых:

Таким образом, написав программу на хорошо известном и простом в освоении языке, пользователь получает возможность с минимальными затратами получать готовые, работающие распределенные системы, исполняющие практически любую бизнес-логику с очень высоким уровнем доверия, в возможностями параллельной обработки и т.д.

В настоящий момент сеть работает в демонстрационном режиме, основные функции доступны с использованием терминала и предоставляемой нами демонстрационной презентации, см ubots intro.

Терминология юботов

  • **UBot* / **Юбот* — специальным образом оформленный и подготовленный код (на JavaScript) — а также содержащий такой код смарт-контракт Universa —, способный запускаться распределённым и децентрализованным образом в сети юботов.
  • UBot server / Сервер юботов (юбот-сервер), иногда “UBot server instance” / «инстанс юбот-сервера» — конкретный уникальный сервер (как с аппаратной, так и программной точек зрения), входящий в сеть юботов и исполняющий код юботов.
  • UBot instance / Инстанс юбота — единичный экземпляр кода юбота, запускающийся на одном сервере юботов.
  • UBot network / Сеть юботов — совокупность всех серверов юботов, распределённо и децентрализованно запускающая на выполнение юботы.

Язык юботов

Юботы в настоящее время пишутся на современном варианте языка JavaScript (ECMA2017), (async/await, классы, splats, etc). В связи с этим для использования в юботе могут быть использованы любые языки, компилирюущие в JavaScript, такие как TypeScript, scala.js, kotlin.js, CoffeeScript и многие другие.

Также в юботы может быть добавлена поддержка wasm, что позволит писать высокопроизводительные юботы на типизированных языках, таких как C++.

Распределённое исполнение

В простейшем случае, при запуске на исполнение метода смарт-контракта, сеть Universa выбирает случайно, на каком инстансе юбот-серверов будет происходить его выполнение. Это дает определённую защиту от атаки на смарт-контракт путем взлома какого-то конкретного инстанса юбот-сервера. Но в общем случае этого недостаточно, поэтому предусмотрен режим распределённого исполнения.

Для каждого метода в смарт-контракте указывается необходимое количество экземпляров для его параллельного исполнения на разных юбот-серверах. Количество может быть указано абсолютным (15 юботов) или относительным (20% сети). Если текущее состояние сети не позволяет исполнить метод, он не будет запущен.

При запуске метода сеть Universa пытается создать сессию исполнения, выбрав необходимое количество юбот-серверов случайным образом, на основе сетевого случайного числа, которое достаточно трудно предсказать или подделать.

Выбранные юбот-сервера (инстансы) запускают указанный контракт, стараясь сделать это одновременно. Каждый инстанс формирует результат (возможно, пустой) и, опционально, изменяют

глобальный стейт контракта. При этом происходит проверка на совпадение результатов и изменений в глобальный стейт. Этот факт проверяется и утверждается сетью Universa; если итоги исполнения не совпали – фиксируется ошибка, и стейт контракта не изменяется.

Таким образом, для получения надежного результата можно указать большое количество параллельно исполняемых инстансов; что, с учетом случайного характера выборки инстансов для сессии, делает атаку взломом инстанса неэффективной.

Представим себе ситуацию, что имеется брокерский скрипт, который должен продать или обменять существенное количество криптовалюты при каком-то состоянии ее курса. Скрипт считывает курс с какого-то внешнего сервиса. Злоумышленник, перехватив контроль над одним из инстансов, подделывает этот курс, перехватывая обращение к сервису на уровне исполняющей системы, имитируя запрос в сеть и ответ. Или даже подделывает сертификат сервиса курса, что вполне возможно в любой стране, которая имеет возможность управлять CA (США, Китай, и многие другие, как мы имеем основания полагать). В этом случае можно спровоцировать вполне валидный смартконтракт на ошибочные действия, потенциально приводящие к большим убыткам.

Применение распределенного исполнения в данном случае исключает такую пробему: часть задействованных инстансов будет исполняться на не скомпрометированных серверах и не в странах с тоталитарным контролем интернета, результаты исполнения будут различаться, и не будут приняты сетью.

В разделе ««распределённое доверие» есть более подробное описание того, как консенсус исполнения юботов используется для заверения транзакций.

Параллельные вычисления

В некоторых случаях может потребоваться повышенная производительность системы, например, при необходимости выполнять одновременно большое количество запросов, или для сложного анализа данных. В этом случае есть возможность запустить независимо любое количество инстансов, которые не могут изменять глобальный стейт, но могут обмениваться при помощи локальных стейтов.

Глобальный стейт, как и локальный, имеет средства для разрешения коллизий при попытке конкурентной модификации. Однако локальных стейтов может быть множество, что обеспечивает возможность конкуретной работы с распределёнными данными.

Распределённое доверие

Распределённое исполнение дает уникальную возможность по увеличении степени доверия к результатам исполнения. Сеть Юботов предоставляет специальный механизм, позволящюий доверять результатам распределенного исполнения права исполнения ролей в смарт-контрактах.

Таким образом, в качестве роли в смарт-контракте можно указать не только владельца приватного ключа, но и результат распределённого исполнения юбота. Предположим, мы хотим дать брокерскому скрипту (наш постоянный пример) право распоряжаться какими то средствами. При этом мы не можем раскрыть скрипту приватный ключ, так как он будет таким образом скомпрометирован. Вместо этого, мы даем возможность скрипту распоряжаться этими средствами при условии достижения консенсуса.

Это означает, что некий метод, запущенный на указанном количестве случайно выбранных серверов, сможет распорядиться средствами, при условии, что все запущенные копии придут к согласию.

Другими словами, консенсус юботов становится ролью в Universa и может совершать любые действия, предусмотренные оперируемым смартконтрактом для этой роли. Консенсус таким образом становится равноправным участником Universa, не обладая приватным ключом.

Распределённое доверие позволяет выполнять самые разные функции; например, следить за внешними данными («обращаться к внешним оракулам»), не опасаясь за их достоверность, так как проверка будет проводиться с произвольных узлов. Это особенно актуально в случае подделки сетевого трафика на отдельных узлах, когда запросы к внешним сервисам на таких узлах получают сфабрикованные ответы, причем сам узел не может диагностировать подделку. Также этот механизм дает определенную гарантию от однократных сбоев.

Атомарное исполнение

Когда смарт-контракт изменяет состояние своего глобального стейта, или локального стейта, операция проводится атомарно. В данном случае это означает, что, если два разных запроса к одному стейту будут менять его состояние, то только один из них завершится успешно (и изменит это состояние), а все остальные будут завершены с ошибкой, и их изменения будут отвергнуты.

Атомарность реализована с использованием механизма критических секций. Юбот, для которого предполагается возможность запуска нескольких параллельных сессий исполнения, может воспользоваться функциями входа в именованную критическую секцию, внутри которой (в рамках указанного имени секции) обеспечивается атомарность исполнения кода Юбота.

То есть, кроме всего прочего, секция обеспечивает функцию lock-for-update для записи в стейт: строго одна сессия исполнения юботов (1 юбот либо консенсус юботов) получает эксклюзивное право на модификацию соответствующих стейтов.

Таким образом, если какой то метод смарт-контракта блокирует на запись глобальный стейт, то система гарантирует, что этот метод исполняется всегда ровно одной сессией. Для параллельного исполнения методы не должны блокировать глобальный стейт и использовать/создавать локальные, а также не должны использовать потоковое хранилище для записи результатов.

Окончательный результат

В духе сети Universa, при исполнении юбота либо фиксируется ошибка исполнения, либо получается финальный результат, который уже не будет отменён со временем. Исполнив метод юбота, не приходится ждать, пока он будет одобрен или подтверждён сетью (он уже ей одобрен), и можно сразу использовать результат.

Однако необходимо понимать, что со временем не будет отменен именно результат, возвращаемое значение. Стейты же, модифицируемые юботом, вполне могут быть изменены последующими вызовами. По этой причине доступ ко стейтам имеют только сами юботы (и средства отладки), во всех же остальных случаях следует использовать возвращаемые значения.

Сессия исполнения

При запуске на исполнение метода юбота, сеть Universa случайным образом выбирает требуемое количество инстансов для одновременного запуска на исполнение метода; это количество определяется в самом исполняемом смарт-контракте юбота (целостность и активность которого, как смарт-контракта Universa, также проверяется).

Для исключения атак на ГСЧ узла случайное число вырабатывается всеми узлами сети Universa при помощи распределённого алгоритма, исключающего возможность предсказать и подделать результат одним из узлов.

Таким образом, при запуске юбота на исполнение, невозможно предугадать, на каких серверах он будет исполнен.

При исполнениии метода сеть Universa контролирует, что результаты предоставлены именно теми серверами, которые были выбраны для сессии, и что достигнут консенсус исполнения. В простейшем случае, все юботы дали одинаковый результат и одинаково изменили стейт(ы). В этом, и только в этом случае, сеть Universa заверяет результаты исполнения.

Во время исполнения, если юбот требует доступ к стейту, соответствующий стейт проверяется на актуальность и заверенность сетью Universa. Если копия стейта на исполняемом стейте устарела, то автоматически будет получена актуальная копия стейта с одного из соседних узлов.

Распределённое хранение

Юботы могут сохранять результаты своей работы в сетевом хранилище. Это делается при помощи глобального стейта и локального стейта. Независимо от того, какой тип хранилища применен, его данные будут заверены сетью Universa и автоматически распределены по всей сети юбот-серверов в фоновом режиме, а при открытии данные будут проверены на актуальность (на актуальность с точки зрения сети Universa), а при необходимости обновлены с других серверов, где найдется актуальная копия.

Распределённое хранилище существенно уменьшает вероятность потери данных, так как даже одного узла, на котором сохранилась актуальная копия, достаточно, чтобы она вновь распространилась по всей сети.

Стоимость хранения данных в сети юботов ниже, чем непосредственно в сети Universa, так как сеть юботов имеет значительно меньшие требования к быстродействию. Такие требования обусловлены тем, что при исполнении контрактов задействуется только часть юбот-серверов, позволяя эффективное параллельное исполнение.

Фоновые процессы

Скрипт юбота может заснуть на какое-то время, например, для выполнения периодических операций или ожидания наступления событий. Так как «спящий» юбот потребляет мало ресурсов, время сна тарифицируется отдельно, причём значительно дешевле, чем время его активного исполнения.

Это позволяет создавать юботов, отслеживающих изменения во внешнем мире, создающих собственные события, запускающих другие юботы по расписанию и т.д. Также это позволяет асинхронно использовать «медленные» внешние сервисы с большими временами ожидания ответов, без оплаты времени ожидания по тарифу активного процесса.

С другой стороны, тарификация фоновых процессов исключает засорение среды выполнения спящими процессами, так как после исчерпания средств, выделенных на их исполнение, они будут остановлены автоматически.

Стейт юбота

Распределённое хранилище разделяется на именованные ячейки, связанные с ориджином смарт-контракта юбота и называемые стейтами. Пространство имен стейтов уникально для каждого смарт-контракта с юботом; оно идентифицируется его ориджином и именем (то есть, у одного смарт-контракта юбота может быть несколько стейтов, связанных с его ориджином, но имеющих разные имена). Имя global зарезервировано за глобальным состоянием контракта.

Стейт обеспечивает автоматическую синхронизацию и транзакционность при изменении.

При открытии стейта для доступа к его данным, система проверяет, что в её распоряжении имеется валидная копия стейта, заверенная сетью Universa, и при необходимости получает её от других узлов.

Глобальный стейт

Каждый юбот всегда имеет один общий стейт юбота, связанный с контрактом юбота и разделяемый всеми методами юбота. Этот стейт называется «глобальным» и используется для хранения общей для всех методов информации и ее безопасного конкуретного изменения. В остальном же это обычный стейт.

Локальные стейты

Юбот (а точнее, смарт-контракт, описывающий юбота) может иметь произвольное количество локальных именованных стейтов, которые могут одновременно использоваться и перезаписываться его методами. Это позволяет методам юбота исполняться параллельно и, в частности, запускать один метод на одновременное исполнение в нескольких сессиях параллельно.

Хранение кортежа

Кортеж — это специальный тип локального стейта, в котором каждый Юбот-сервер в рамках сессии исполнения записывает свои данные, которые (в отличие от случая стандартного стейта) не обязаны совпадать с результатами, записанными остальными Юбот-серверами. Собранный кортеж затем проверяется на идентичность на всех Юбот-серверах текущей сессии, после чего заверяется основной сетью Universa.

Такой тип хранилища позволяет позволяет Юбот-серверам сессии обмениваться данными в процессе исполнения.

Тарификация юботов

При работе юботов тарифицируются независимо друг от друга:

  • запуск сессии исполнения;
  • время исполнения собственно кода;
  • время ожидания (idle), например, при выполнении запросов ко внешним серверам или при организации фоновых процессов;
  • распределенное хранение данных в разного вида хранилищах;
  • запись в хранилище (стандартное/кортеж);
  • исходящие http-запросы.

Для простоты можно считать, что каждый юбот имеет своего рода депозит, откуда списываются необходимые расходы. Когда депозит исчерпан — исполнение методов юбота становится невозможным. Данные хранятся некоторое время после исчерпания предельного срока (в заблокированном состоянии, для разблокировки достаточно оплатить фактическое время хранения). Невостребованные заблокированные данные удаляются.

Демонстрация

Доступна демо-сеть юботов, к которой написано демонстрационное приложение, позволяющее в данный момент бесплатно создавать и исполнять юботов в тестовой сети: