Pull to refresh

rholang — язык программирования для распределенных систем

Reading time5 min
Views2.3K

RChain — распределенная вычислительная блокчейн-платформа. Как ethereum, только на порядок шустрее. В теории, ее можно масштабировать до бесконечности, первые реализации на практике позволяют обрабатывать до 40 тысяч транзакций в секунду. Технические подробности — в документе про архитектуру.



Контракты на платформе RChain написаны на языке rholang, с которым я бы хотел познакомить аудиторию хабра. Попробовать написать и выполнить программы на этом языке можно либо скачав и запустив Rchain-ноду через докер, либо воспользовавшись интерпретатором на сайте.


Полный туториал


Роланг (или просто ро) — это "процессо-ориентированный" язык. Все вычисления на нем выполняются при помощи передачи сообщений между "каналами". На каналах хранятся множества сообщений. Ро — полностью асинхронный язык, поэтому очередность, с какой сообщения поступают на каналы, не играет никакой роли. Например, с канала можно считать сообщение и затем выполнить над этим сообщением какую-нибудь операцию. Однако нельзя отправить сообщение и потом сделать что-нибудь после того, как сообщение будет получено. По крайней мере, не прописывая отдельным образом ожидание сообщения с подтверждением доставки. Стоит отметить, что в этом туториале мы будем использовать термины имя и канал как синонимы. В $rho-$алгебре, на которой основан rholang, используется термин имя, но так как мы можем с их помощью посылать и получать данные, то семантически они похожи на каналы.


Контракты и отправка данных


1 contract @"HelloWorld"(return) = {
2   return!("Hello, World!")
3 } |
4 new myChannel in {
5   @"HelloWorld"!(*myChannel)
6 }

  • Программа на ро — единый процесс состоящий из нескольких частей, которые исполняются синхронно. Это процессс запускается при помощи создания контракта с именем @"HelloWorld". Создание контракта запускает процесс, который вызывает копию своего "тела" каждый раз, когда он получает сообщение. Стоит отметить, что в роланге все процессы могут быть "цитированы" при помощи оператора @ и превратиться в канал. Строки — это просто особые процессы, и мы можем процитировать любой процесс и создать таким образом канал. За хранение данных на платформе RChain отвечает библиотека rspace, написанная на Scala.
  • На канале return мы посылаем строку "Hello, World!"
  • Конструкция new ... in позволяет создать новый приватный канал. Никакой иной процесс не может отправлять или получать с этого канала сообщения. Каждое исключение необходимо указывать отдельно, отправляя имя_канала другому процессу.
  • Мы посылаем канал myChannel контракту по адресу @"HelloWorld". Оператор * означает "обратное цитирование" ("unquoting") канала и возвращает исходный процесс. В ро можно только посылать процессы по каналам, нельзя напрямую посылать каналы на каналы. Такими образом мы превращаем приватный канал в процесс перед его отправкой.

Получение данных


1 contract @"HelloAgain"(_) = {
2   new chan in {
3     chan!("Hello again, world!") |
4     for (@text <- chan) { Nil }
5   }
6 } | @"HelloAgain"!(Nil)

  • Контракты обладают хотя бы одним параметром, но мы можем обойти это, указав подстановочный символ_.
  • Мы создаем новый канал chan.
  • Мы посылаем строчный процесс "Hello again, world!" по новому каналу.
  • Мы слушаем новый канал и ждем единственного сообщения. Операция for остается заблокированной до тех пор, пока на канале chan не будет сообщения. В ро по каналам можно получать только имена, а вот отправлять можно и цитированные процессы. Присвоение слева от выражения <- представляет собой паттерн имен.В данном примере это @text, что означает, что получаемое имя является цитированным процессом и мы хотим связать этот процесс к свободной текстовой переменной. Операция for ведет себя аналогично: она может читать только одно сообщение и затем становится телом, а не вызывает копию себя в каждом сообщения. В таком случае мы ничего не делаем в for и превращаем его в остановленный процесс Nil, в принципе мы можем совершить с текстом на канале chan и другие операции.

Изменение состояний


 1 new MakeCell in {
 2   // Makes a single cell in which you can store values
 3   contract MakeCell(@init, get, set) = {
 4     new valueStore in {
 5       valueStore!(init) |
 6       contract get(ack) = {
 7         for(@value <- valueStore) {
 8           valueStore!(value) | ack!(value)
 9         }
10       } |
11       contract set(@newValue, ack) = {
12         for(_ <- valueStore) {
13           valueStore!(newValue) | ack!(true)
14         }
15       }
16     }
17   } |
18   // Cell usage.
19   new myGet, mySet in {
20     MakeCell!(123, *myGet, *mySet) |
21     new ack in {
22       myGet!(*ack) |
23       for (@result <- ack) {
24         //result now contains the value 123
25         mySet!(456, *ack) |
26         for (_ <- ack) {
27           myGet!(*ack) |
28           for (@result <- ack) {
29             //result now contains the value 456
30             Nil
31           }
32         }
33       }
34     }
35   }
36 }

  1. Мы создаем новый канал MakeCell и затем используем его на строке 3 как имя внутреннего контракта. Никакой другой процесс, кроме кода в этом лексическом окружении может его вызвать.
  2. Контракту MakeCell необходимы три аргумента. Первый аргумент — это значение, которое будет содержать эта ячейка. Второй и третий — каналы, по которым ячейка будет получать запросы на чтение и запись. Отметим, что первый аргумент должен быть процессом, а второй и третий — именами. Так как имена всегда посылают по каналам, то первым аргументом станет паттерн, начинающийся с @, который показывает на то, что получаемое имя является цитированным процессом и мы хотим связать этот процесс с переменной.
  3. Чтобы сохранить значение, мы создаем новый канал. Этот канал будет содержать максимум одно значение, текущее значение ячейки.
  4. До этой линии на канале valueStore нет ни одного сообщения. После того, как мы установим начальное значение, это значение станет единственным на этом канале.
  5. Мы запускаем контракт, который слушает на канале чтения. Каждый раз, когда получено сообщение, выполняется тело конткракта.
  6. Мы блокируем контракт, пока ты не получим сообщение на канале valueStore. Так как на канале valueStore может ожидать не более одного сообщения, прочитывание сообщения является своеобразным замком.
  7. Мы снова передаем текущее значение канал valueStore, открывая обработку других сообщений и снимая блок. Теперь мы передаем текущее значение обратно клиенту на канале ack.
  8. Синхронно с get контрактом мы запускаем контракт, который слушает на set.
  9. Мы блокируем его, до тех пор, пока не появится сообщение на valueStore, а затем читаем его. Мы выбрасываем сообщение, которое прочитали.
  10. Мы отправляем новое значение в хранилище на канале valueStore и даем сигнал, что операция завершилась.

  • 18-36) Код, который показывает создание ячейки, установку начального значения 123, считывание этого значения, установку значения 456, получение этого значения.

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


Заключение


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


Полный туториал.

Tags:
Hubs:
Total votes 5: ↑3 and ↓2+1
Comments5

Articles