Information Security
Cryptography
Programming
Cryptocurrencies
June 27

Plasma Cash Chain как решение трилеммы масштабируемости в блокчейн

Tutorial
Добрый день, уважаемые читатели!

Данная статья посвящена Plasma Cash Chain и проливает свет на следующие темы:

  • трилемма масштабируемости и способы ее решения;
  • структуры данных чайлд чейна и их отображение в рутчейне;
  • реализация ввода в рутчейн;
  • реализация вывода из рутчейна.

Компания Opporty использовала язык программирования Javascript для реализации чайлдчейна, а также Solidity для рутчейна. Примеры кода приводятся на этих языках.



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

Смарт-контракты вносят бизнес-логику в децентрализованные сети. Это позволяет строить новые приложения DAPP.

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

Современные децентрализироанные блокчейны имеют несколько недостатков. Основной из них — это масштабируемость. Ethereum обрабатывает около 20 tx/s. Этого недостаточно в современных финансовых реалиях. При этом Ethereum имеет максимально высокую степень защиты от взломов и поломок сети. Другие криптовалюты и системы, построенные на блокчейне, не имеют такой высокой степени децентрализации, что снижает доверие в сети.

Трилемма масштабируемости


Существует трилемма масштабируемости блокчейна, включающая три компонента:

  • децентрализация;
  • безопасность;
  • масштабируемость.

Децентрализация в трилемме


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

Для внесения ясности необходимо рассказать о наиболее централизованных организациях. Обычно вместо блокчейна используется простая база данных. Управляют такой организацией специальные администраторы. Все транзакции могут быть отменены путем ручного вмешательства.

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

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

Безопасность в трилемме


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

В данном случае, децентрализация и безопасность идут рука об руку. Чем больше узлов, тем меньше сеть зависит от централизованной стороны и, следовательно, меньше риск наличия центральной точки отказа. Однако, существует множество других векторов атак, которые представляют опасность для децентрализованных сетей, в том числе:
> Атака 50% — объект, которому принадлежит более 50% от общего числа неоплаченных токенов, фактически владеет сетью;
> Sybil Attack — пользователь может сгенерировать множество идентификаторов в системе, чтобы эффективно контролировать значительную долю во владении и/или принятии решений в сети;
> DDoS — распределенная атака типа «отказ в обслуживании» (DDoS) происходит, когда есть намерение нарушить трафик в сети, заполняя сеть вредоносными транзакциями;
> Атака сговора — один или несколько объектов (или узлов) решают объединиться для выполнения какой-либо вредоносной операции в сети.

Масштабируемость в трилемме


Степень масштабируемости важна, поскольку она определяет конечную пропускную способность, другими словами, верхний предел размера сети. Самый важный вопрос, который следует задать при оценке сети: “Сколько пользователей может выдержать эта система?” Биткоин в настоящее время имеет от 2,9 до 5,8 миллионов держателей кошельков. ЕОС имеет несколько тысяч участников.

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

“Святой Грааль” в блокчейн технологии


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

  • Децентрализация (каждый участник имеет доступ только к ресурсам O ©, то есть к обычному ноутбуку или небольшому VPS);
  • Масштабируемость (способность обрабатывать транзакции O (n) > O ©);
  • Безопасность (защита от злоумышленников с использованием ресурсов O (n)).


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

“Святой Грааль” в блокчейн технологии значит объединение всех трех аспектов.
В большинстве текущих проектов, работающих с криптовалютами, достигаются два базовых свойства: децентрализация и безопасность. При этом масштабируемость страдает.

Перспективные варианты решения трилеммы


Proof of Stake (PoS)


Proof of Stake (PoS) обеспечивает потенциальное повышение масштабируемости. POS заменяет майнинг криптовалюты, основанный на системе Proof of Work (PoW). Выбор валидатора происходит очень быстро — детерминированным способом. При этом нет затрат электроэнергии и это экологически выгодно.

Sidechains


В виртуальной сети Ethereum существует возможность создания побочной сети, в которой проект может обрабатывать свои отдельные транзакции, а затем записывать только начальный и конечный результаты в сеть Ethereum. Это уменьшает нагрузку на EVM, но вызывает большее доверие к управлению sidechain. Таким образом, доверие третьей стороне уменьшает децентрализацию.

Sharding


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

Увеличение размера блока


Litecoin и Bitcoin Cash (BCH) являются «форками» для биткойн-блокчейна. Форкинг в основном копирует один блокчейн. После разветвления можно вносить изменения. Как LTC, так и BCH увеличили размер каждого блока, что позволило хранить больше транзакций на блок, увеличивая тем самым скорость обработки транзакций.

Lightning Network


Самым первым решением для сайдчейна был лайтнинг. Основная идея Lightning Network состоит в том, что в блокчейне следует записывать не все транзакции, так как это перегружает сеть. Если пользователи переводят средства друг другу несколько раз, то регистрировать каждый перевод необязательно. Достаточно просто открыть своего рода платежный канал и записать данные о его открытии в блокчейн. Этот канал будет оставаться открытым сколько понадобится. Когда будет необходимо его закрыть, результат всех транзакций, произведенных в этом канале, просто запишется в блокчейн. Следуя этой идее, можно создать целую сеть каналов для платежей. Тогда транзакции в блокчейне будут использоваться гораздо реже.

Канал для платежей — это всего лишь объединение нескольких транзакций. Канал может закрывать любой его участник. Это действие будет подобно открытию сейфа, позволяющему забрать принадлежащие участникам средства и записать в блокчейн данные об их переводе.
Действительно мощной эта технология становится тогда, когда несколько подобных каналов объединяются в сеть, которую называют The Lightning Network. Эта сеть специально построена для Bitcoin.

Raiden network


Для Ethereum наиболее известным аналогом Lightning является Raiden Network.
Это решение для масштабирования вне основного блокчейна. Оно совместимо с передачей токенов ERC-20 в двунаправленных каналах оплаты.

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

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

Оптимальное решение


Идеологи Lightning создали новую концепцию чайлдчейна, которая решает проблемы скорости блокчейна.

Opporty практически реализует концепцию Plasma и Plasma Cash.

Плазма — это множество смарт-контрактов, которые выполняются поверх рутчейна Ethereum и состоят из сети дочерних цепочек, соединенных с корневой цепочкой в ​​иерархической древовидной структуре.

То есть защищенность рутчейна Ethereum используется для оптимизации масштабируемости.

Plasma Cash: вариант реализации от Opporty


Opporty использует реализацию Plasma Cash в первой версии.

Эта модель является самой эффективной имплементацией плазмы с точки зрения scalability.
Plasma Cash — это система, основанная на использовании уникальных идентификаторов для каждого токена в цепочке Plasma. То есть применяется NFT и токены в сети получают уникальные серийные номера.

Особенности Plasma Cash:

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

Недостаток:

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

Структура транзакции в Opporty Plasma Cash


Компания Opporty использовала язык Javascript для реализации чайлдчейна. Каждая транзакция в Opporty Plasma Cash является подобной структурой:

const transactionFields = [
{name: 'prevHash'},
{name: 'prevBlock', int: true, default: 0},
{name: 'tokenId', isDecimal: true},
{name: 'newOwner'},
{name: 'type'},
{name: 'signature'},
]

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

Далее, для того чтобы собрать блок и получить хеш рутчейна, используется специальный вид дерева Patricia Merkle Trie. Это же дерево используется в Ethereum. Оно имеет сжатый вид. При этом все так же можно получать пруфы включения или невключения транзакции в блок.
Signature — это подпись на эллиптических кривых.

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

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

Блок выглядит следующим образом:

const blockFields = [
{name: 'prevHash'},
{name: 'blockNum', isDecimal: true},
{name: 'transactions'},
{name: 'merkleRoot'},
{name: 'time'}
]

На базовом уровне, блокчейн — это просто цепочка блоков со ссылкой на предыдущий блок. Такая структура дает возможность получить свойство неизменности, то есть не переписывания истории. merkleRoot дает возможность записывать чекпоинты в рутчейн.

В рутчейн, на уровне смарт контракта, это выглядит следующим образом (язык Solidity):

/*
* Block structure (represents one block in a chain)
*/
struct Block {
uint block_num;
bytes32 merkle_root;
uint time;
    
 
/*
* Transaction structure (decoded from RLP form)
*/
struct Transaction {
bytes32 prevhash;
uint prev_block;
uint token_id;
address new_owner;
}
 

Кодирование осуществляется с помощью функций кодирование / декодирование — сериализация / десериализация RLP.

Способы ввода в Plasma Cash


Любой человек может ввести средства в Plasma Cash, просто перекинув эфир в смарт контракт. В итоге будет получен OPP токен в определенной позиции tokenId.

Вот реализация на языке Solidity:

function deposit() public payable {
uint token_id = uint(keccak256(msg.sender, msg.value, deposit_blk));
// token.index = deposit_blk;
tokens[token_id] = msg.value;
deposit_blk += 1;
emit DepositAdded(msg.sender, msg.value, token_id, current_blk);
} 

То есть tokenId генерируется как случайное число (хеш). Далее генерируется событие, которое сканируется в дочерней цепочке.

Способы вывода в Plasma Cash


Каждый человек может вывести свой токен, предоставив последние две транзакции в истории владения токена.

Реализация выхода из рутчейна:

function startExit(uint block_num, bytes tx1, bytes tx0, bytes proof1, bytes proof0) public returns (uint exit_id) {
require(checkPatriciaProof(keccak256(tx1), childChain[block_num].merkle_root, proof1));
 
bytes32 prev_hash;
uint prev_blk;
uint token_id;
address new_owner;
(prev_hash, prev_blk, token_id, new_owner,) = getTransactionFromRLP(tx1);
    
require(msg.sender == new_owner);
        
require(tokens[token_id] > 0);
bytes32 hashPrevTx = keccak256(tx0);
require(checkPatriciaProof(hashPrevTx, childChain[prev_blk].merkle_root, proof0));
require(prev_hash == hashPrevTx);
 
Exit storage record = exitRecords[token_id];
require(record.block_num == 0);
 
record.block_num = block_num;
record.new_owner = msg.sender;
record.prev_block = prev_blk;
 
if (childChain[block_num].time > block.timestamp - week)
record.priority = childChain[block_num].time;
else
record.priority = block.timestamp - week;
 
exits.add(record.priority);
exit_ids[record.priority].push(token_id);
 
emit ExitAdded(msg.sender, record.priority, token_id);
return token_id;
}
 

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

Вывод может быть оспорен тремя способами:

  • предоставление подтверждения spend на транзакции:

function challengeSpent(uint exit_id, uint blk_num, bytes tx1, bytes proof) public { 
require(checkPatriciaProof(keccak256(tx1), childChain[blk_num].merkle_root, proof));
 
Exit memory record = exitRecords[exit_id];
require(record.block_num > 0);
uint prev_block;
uint token_id;
(, prev_block , token_id, ) = getTransactionFromRLP(tx1);
require(tokens[token_id] > 0);
require(prev_block == record.block_num && record.block_num < blk_num);
require(token_id == exit_id);
 
exit_ids[record.priority].remove(exit_id);
delete exitRecords[exit_id];
emit ExitChallengedEvent(exit_id);
}    

Если существует транзакция, которая уже тратит выводимый токен, то такой вывод будет отменен!

  • предоставление подтверждения расходов на предыдущую транзакцию:

/*
* Challenge exit by providing
* a proof of a transaction spending P(C) that appears before C
*/
function challengeDoubleSpend(uint exit_id, uint blk_num, bytes tx1, bytes proof) public { 
require(checkPatriciaProof(keccak256(tx1), childChain[blk_num].merkle_root, proof));
Exit memory record = exitRecords[exit_id];
require(record.block_num > 0);
// bytes32 prev_hash; 
uint prev_block;
uint token_id; 
(, prev_block , token_id, ) = getTransactionFromRLP(tx1);
require(tokens[token_id] > 0);
// check if token double spent
require(prev_block == record.prev_block && blk_num < record.block_num);
// require(token_id == exit_id);
exit_ids[record.priority].remove(exit_id);
delete exitRecords[exit_id];
emit ExitChallengedEvent(exit_id);
}   

Это такая же проверка как если бы токен был потрачен до вывода. Сначала проверяется наличие транзакции в корневом хеше. Далее мы удаляем вывод, если он уже был потрачен.

  • предоставление транзакции в истории транзакций токена до нее.

Это может быть неправильная история, поэтому нужно подтвердить ее дочерней транзакцией:

//  */
function challengeInvalidHistory(uint exit_id, uint blk_num, bytes tx0, bytes proof) public { 
// check if proof is valid
require(checkPatriciaProof(keccak256(tx0), childChain[blk_num].merkle_root, proof));
        
Exit memory record = exitRecords[exit_id];
require(record.block_num > 0);
 
bytes32 prev_hash; 
uint token_id; 
(prev_hash, , token_id, ) = getTransactionFromRLP(tx0);
 
//require(exit_id == token_id);
require(tokens[token_id] > 0);
 
// transaction should be before exit tx in history
require(blk_num < record.block_num - 1);
 
challenged[exit_id] = blk_num;
emit ChallengedInvalidHistory(exit_id, token_id);
}
 

Вызов первого и второго сценария блокирует вывод немедленно.

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

/*
* Respond to invalid history challenge by providing
* the direct child of C*, which must be either equal to or before P( C )
*/
function respondChallenge(uint exit_id, uint blk_num, bytes childtx, bytes proof) public {
require(challenged[exit_id] > 0);
Exit memory record = exitRecords[exit_id];
require(record.block_num > 0);
require(checkPatriciaProof(keccak256(childtx), childChain[blk_num].merkle_root, proof));
// get transaction from rlpencoded form
bytes32 prev_hash; 
uint prev_block;
uint token_id; 
(prev_hash, prev_block, token_id, ) = getTransactionFromRLP(childtx);
// if direct child
if (prev_block == challenged[exit_id] ) {
if (blk_num <= record.prev_block && token_id == exit_id ) {
delete challenged[exit_id];
emit ExitRespondedEvent(exit_id);
} else {
exit_ids[record.priority].remove(exit_id);
delete exitRecords[exit_id];
emit ExitChallengedEvent(exit_id);
}
}
} 

То есть, если получена правильная дочерняя транзакция, вывод оспорен и остается в очереди!
После построения части протокола Opporty Plasma Cash был сделан следующий вывод:
Данный протокол обеспечивает защищенность за счет рутчейна в Ethereum.

Путем осложнения процедуры ввода и вывода с рутчейна и сжатия состояния (блоков транзакций) были рассмотрены все способы вывода и ввода в рутчейн, а также были исследованы базовые структуры данных: транзакции и блоки.

Используя сайдчейн на базе сети Ethereum, можно значительно ускорить проведение транзакций. Компания Opporty получила до 300000 транзакций в секунду на одном операторе. Это намного больше чем то, что могут обеспечить нынешние платежные системы.

Несмотря на некоторые проблемы data availability, оператор обеспечивает высокий уровень стабильности блокчейна и дает возможность совершать эффективные международные бизнес транзакции.

Plasma Cash привносит огромное увеличение scalability. Поэтому компания Opporty использует Plasma как часть своего протокола PoE.

Полезные ссылки


  1. White Paper Plasma
  2. Git hub
  3. Use cases and architecture description
  4. Lightning Network Paper

+8
795 5
Comments 14