Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
blockchained
7 лет назад

📒 EOS - Журнал разработчика. Звездная дата 201707.3 (@dan)

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

Проблема текущего баланса

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

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

Другой возможный вариант заключается в использовании оракула, который будет уведомлять ваш контракт о балансе, одновременно передавая сообщение валютному контракту. Если оракул передаёт неверные данные, валютный контракт отклоняет транзакцию, поэтому ваш контракт может доверять полученной информации о балансе. Опять же, это позволяет узнать баланс в момент совершения транзакции, но создает новую проблему: если в это же время действия другого пользователя изменят ваш баланс, ваша транзакция может оказаться недействительной.

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

Новый подход к определению проблемы

В приведенной выше модели все данные принадлежат одному аккаунту и его коду. Это делает каждый аккаунт, равно как и его блокчейн, а также передачу данных между аккаунтами ненадёжными. К счастью, разработчики графических процессоров (GPU) использовали другой принцип распараллеливания: SMID – одиночный поток команд, множество потоков данных. В общих чертах, графический процессор выполняет один и тот же код над множеством независимых экземпляров данных. Каждый пиксель и / или вершина модифицируются одним и тем же набором инструкций.

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

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

Что собой представляет такой контракт?

void apply_simplecoin_transfer() {
   static Transfer message;
   static uint64_t balance = 0;
   load( thisContract(), “balance”, balance );
   
   readMessage( message  );
   requireNotify( {message.from, message.to} );
   
   if( thisAccount() == message.to ) {
      balance += message.amount;
   } else if ( thisAccount() == message.from ) {
      assert( message.amount < balance, "insufficient funds" )
      balance -= message.amount;
   }
   
   if( balance ) store( “balance”, balance );
   else remove( “balance”, balance );
}

Обратите внимание, в нем существует несколько нюансов:

  1. Метод apply изменяет только один баланс;
  2. Поведение контракта зависит от значения thisAccount();
  3. Метод load() занимает дополнительный параметр, определяющий текущий контракт;
  4. Методы store() и remove() всегда используют ключ «balance», а не ключ аккаунта.

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

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

Что это нам даёт?

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

Предположим, вы бы хотели создать социально-медийную платформу, где вес голоса пропорционален текущему балансу. Для этого требуется, чтобы аккаунт социальной сети имел возможность считывать ваш баланс и изменять итоги голосования за посты других участников. В идеале было бы здорово, если бы пользователь @alice могла бы голосовать за @bob, а @sam – за пользователя @bill одновременно. Это может быть достигнуто следующим образом:

void apply_social_vote() {
   readMessage( vote );
   if( vote.for == thisContract() ) {
      load( thisContract(), vote.postid, post );
      post.totalvotes += vote.weight;
      store( vote.postid, post );
   } 
   
   if( vote.voter == thisAccount() ) {
      static uint64_t balance = 0;
      load( "simplecoin", "balance", balance );
      assert( balance >= vote.weight, “insufficient balance for vote weight” );
   }
}

В этом случае программный код контракта выполняет два разных действия, в зависимости от того, с какими данными он работает. Если он работает с получателем голосов, то увеличивает итоги голосования. Если с отправителем – то просто подтверждает условие vote.weight <= balance.

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

Вместе с тем, несмотря на невозможность считать чужой баланс при изменении своего состояния, вы можете считать собственный баланс из контракта «simplecoin» в рамках контракта «social».

Почему всё так сложно?

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

Новая надежда

Существует способ разрешить одному аккаунту считывать баланс другого; транзакция может объявить, что ей требуется доступ для чтения. Планировщик транзакций блокчейна может обеспечить, чтобы никто из тех, кому требуются права доступа на запись, не выполнял код одновременно. Применение старого подхода к “simplecoin” (один контракт, владеющий всеми данными) создало бы сильную перегрузку контракта, так как все, кто должен считать данные, блокировали бы тех, кто хочет их перезаписать, и отдельный контракт оказался бы для всех узким местом.

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

Заключение

Параллелизм – это сложно, особенно, когда вам нужно, чтобы всё оставалось детерминированным при организации доступа к совместно используемым данным. К счастью, доступны решения и шаблоны проектирования, проверенные разработчиками GPU и алгоритмов компьютерной графики.


Отказ от ответственности: В этом посте нет ничего, что следует рассматривать как предлагаемую конкретную функциональную возможность или алгоритм работы программного обеспечения EOS.IO. Все проектные решения разработки программного обеспечения могут быть изменены по мере необходимости.


Свежие новости в Телеграм: t.me/EOS_RU


Переведено @rusteemitblog

Оригинал поста: ЗДЕСЬ


Поддержите делегата blockchained на Голосе

1
350.863 GOLOS
На Golos с January 2017
Комментарии (4)
Сортировать по:
Сначала старые