При сотрудничестве с Civil одним из самых больших запросов от редакций новостей, публикующих в экосистеме, была устойчивость к цензуре, способ хранения всех документов внутри самого блокчейна.
Несколько требований:
- Любой должен иметь возможность получить указатель всех перечисленных статей.
- Статьи должны быть доступны для поиска во внешнем интерфейсе
- Затраты не могут быть непомерно высокими, менее 1 доллара за статью.
Мы приступили к исследованию проблемы и в итоге пришли к следующей технической схеме. Каждая служба новостей будет отдельным смарт-контрактом в блокчейне, а каждая статья будет создавать событие. Таким образом, любой может просмотреть все события и перестроить индекс на стороне клиента.
Мы повторяли процесс хранения статей, пока не нашли решение, которое стоит всего 0,04 доллара США за 100 предложений.
Сначала мы выбрали самый простой способ — хранить статью прямо в массиве Storage смарт-контракта.
contract Example { event ArticleStored(uint id); string[] public articleArray; function storeArticle(string calldata content) external { emit ArticleAddedInArray(articleArray.length); articleArray.push(content); } }
Что в итоге стоило около $1,42 за статью, что слишком дорого.
Согласно Желтой бумаге Эфириума за байт статьи нужно платить трижды.
- Стоимость данных о транзакциях
- Стоимость загрузки данных транзакции в память
- Стоимость хранения данных из памяти в хранилище
Это решение самое дорогое, но единственное, если вы хотите, чтобы другие контракты сами читали статьи.
После этого мы сохранили статью в самом событии. Хранение данных в событиях значительно дешевле, чем хранение чего-либо в постоянном хранилище.
Недостатком здесь является то, что теперь статью можно извлечь только из-за пределов блокчейна, со стороны клиента.
contract Example { event ArticleStored(string content); function storeArticle(string calldata content) external { emit ArticleStored(content); } }
Нам по-прежнему приходится платить трижды за статью, разница здесь в том, что хранилище Event стоит всего 32 газа за слово по сравнению с 20000 газа для хранилища. Это уже огромная экономия средств.
Мы хотим заплатить за статью только один раз и поместить ее только в сам вызов функции.
contract Example { event ArticleStored(); function storeArticle() external { emit ArticleStored(); } }
Для этого нам нужно понять структуру вызовов, которую мы используем.
Во-первых, это закодированные инструкции смарт-контракта, это то, что смарт-контракт читает, чтобы понять, какую функцию вызывать. Затем идет сама статья в формате gzip и, наконец, длина статьи в формате gzip, отформатированная до 8 байт.
Вот как отправить транзакцию с помощью Truffle.
// Node has a built-in deflate function const deflatedArticle = (await deflate(ARTICLE)).toString("hex"); // Data to call the function of the contract const encodedABI = instance.contract.methods.inTransaction().encodeABI(); // Length of the article padded to 8 bytes so it's constant in length const hexLength = deflatedArticle.length.toString(16).padStart(16, "0"); const txData = encodedABI + deflatedArticle + hexLength; const web3 = Example.web3; await web3.eth.sendTransaction({ from: (await web3.eth.getAccounts())[0], to: instance.address, data: txData, gas: 4000000 // A random big number }));
И в итоге мы получаем статью, которая навсегда хранится в блокчейне, доступна для поиска и стоит всего 0,04 доллара США!