{"id":13466,"url":"\/distributions\/13466\/click?bit=1&hash=891d339b00b86120568ea8e4296ded112a42876a976e2fd335004400f35cbd30","title":"\u0427\u0442\u043e \u0441\u043c\u043e\u0442\u0440\u044f\u0442, \u0447\u0438\u0442\u0430\u044e\u0442 \u0438 \u043a\u0443\u0434\u0430 \u0445\u043e\u0434\u044f\u0442 \u0432\u0430\u0448\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u044b?","buttonText":"\u0423\u0437\u043d\u0430\u0442\u044c","imageUuid":"24bb823c-c595-5fc8-be0f-fba9e89237c2","isPaidAndBannersEnabled":false}
Крипто
Егор Абрамов

Взгляд венчурного инвестора на Блокчейн | 10: Блокчейн своими руками в 50 строках кода

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

За основу я взял вот эту статью с Medium: Blockchain Explained in 50 Lines of Code. По сути, этот пост – переложение статьи с отсылками к моим предыдущим постам плюс мой искрометный юмор.

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

Опишем наш модельный блокчейн

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

Каждый блок обычно содержит такую информацию:

  • Контент / данные: это может быть что угодно, например данные о транзакции

  • Временная метка: дата добавления блока в блокчейн

  • Хэш блока: уникальный ID самого блока, зависящий от его содержимого

  • Решение криптографической задачи: стандартное название этого поля – nonce (number once)

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

Подготовка рабочего места

Код будем писать на Javascript, это на сегодня, пожалуй, наиболее популярный язык, который используют разработчики в области Web3. Он нативно работает с web-приложениями.

Для того, чтобы не просто написать, но и запустить блокчейн, вам нужно будет использовать ту или иную среду разработки Javascript. Те, кто уже программирует, будут использовать Node.js на своем компьютере. Но поскольку этот пост для более широкой аудитории, то я рекомендую использовать свободный онлайн редактор и среду разработки. Например, Replit. Вы просто копируете весь код туда – и получаете результат. Там же с кодом можно поэкспериментировать.

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

Писать сам код можно в любом текстовом редакторе, я сам предпочитаю Sublime.

Давайте уже начнем кодить!

Создаем класс Block

Сначала открываем пустой файл и создаем класс Block.

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

Внутри конструктора мы инициализируем блок начальными параметрами. Мы выставляем timeStamp на текущее системное время, а nonce – на ноль.

Хэш самого блока сразу же рассчитывается в зависимости от его содержимого. Таким образом достигается неизменность блокчейна (см подробнее этот пост). То есть для изменения одного блока в прошлом, хакеру придется пересчитать и поменять все хэши последующих блоков. Это займет ОЧЕНЬ много времени в реальном блокчейне.

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

Теперь давайте посмотрим на функцию calculateHash.

Эта функция нужна для того, чтобы генерировать хэш блока на базе его содержимого: хэша предыдущего блока, контента, текущей даты и результата решения криптографической задачи. Тут мы используем функцию hash, которая реализует алгоритм SHA256. На вход ей направляется одна символьная строка, которая получается простым объединением всех данных, которые есть в блоке.

Как я писал ранее, я нашел в сети полный код функции SHA256. Если вам интересно как она работает, смотрите в конце поста её полный код.

Теперь давайте посмотрим на последнюю функции класса Block: на функцию майнинга mine.

Функция mine продолжает увеличивать nonce до тех пор, пока результирующий Хэш не будет начинаться с заданного числа нулей (это число называется difficulty). Чем выше параметр difficulty, тем сложнее найти решение криптозадачи, потому что решение находится методом перебора.

Хэш блока в блокчейне Биткоина сегодня имеет 18 лидирующих нулей (difficulty = 18). С таким уровнем сложности вся сеть биткоина тратит на решение одной криптозадачи около 10 минут.

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

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

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

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

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

Создаем класс Blockchain

Давайте создадим новый класс Blockchain:

Наш блокчейн содержит один массив, который называется chain. Мы начнем эту цепочку с нулевого блока, у которого нет предшественников – он называется genesisBlock. При его создании мы передаем 0 в качестве хэша предыдущего блока.

Genesis block есть и в Биткоине, его в 2009 году создал Сатоши Накамото, когда дал жизнь блокчейну Биткоина. Кстати, вот этот первый блок. Он был создан 03 января 2009 года. В результате Сатоши, как майнер, заработал 50 биткоинов.

Возвращаемся к нашему блокчейну. Теперь мы имплементируем функцию addBlock – чтобы мы могли добавлять блоки к цепочке.

Эта функция принимает Контент нового блока в качестве входного параметра. Блок добавляется на базе Контента и хэша последнего блока, сохраненного в цепочке. Как вы помните, для того чтобы добавить новый блок нам необходимо решить криптографическую задачу – в данном случае вызвать функцию mine(difficulty).

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

Наконец, нам нужно реализовать функцию isValid. Она нужна, чтобы можно было проверить валидность цепочки блоков – что какой-то из блоков не был подменен и все правила сети выполняются.

Эта функция пробегает через каждый блок в цепочке и проверяет, что во всех случаях хэш, указанный как хэш предыдущего блока, в реальности равен хэшу предыдущего блока. А также что решения всех криптозадач во всех блоках верные. Если все верно, возвращается значение true.

Поздравляю, мы успешно написали собственный блокчейн! Посмотрим, как это работает на конкретном примере.

Пример работы программы

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

Вывод этой программы будет выглядеть примерно так:

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

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

Полный код

Ниже для справки приведен весь код, включая код функции hash. Спасибо и до новых встреч!

class Block { constructor(previousHash, data) { this.data = data; this.hash = this.calculateHash(); this.previousHash = previousHash; this.timeStamp = new Date(); this.nonce = 0; } calculateHash() { return hash( this.previousHash + JSON.stringify(this.data) + this.timeStamp + this.nonce ).toString(); } mine(difficulty) { while (!this.hash.startsWith("0".repeat(difficulty))) { this.nonce++; this.hash = this.calculateHash(); } } } class Blockchain { constructor() { let genesisBlock = new Block("0", { isGenesis: true }); this.chain = [genesisBlock]; } addBlock(data) { let lastBlock = this.chain[this.chain.length - 1]; let newBlock = new Block(lastBlock.hash, data); newBlock.mine(2); // find a hash for new block this.chain.push(newBlock); } isValid() { for (let i = 1; i < this.chain.length; i++) { const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1]; if (currentBlock.hash != currentBlock.calculateHash()) return false; if (currentBlock.previousHash != previousBlock.hash) return false; } return true; } } // SHA256 realisation var hash = function sha256(ascii) { function rightRotate(value, amount) { return (value>>>amount) | (value<<(32 - amount)); }; var mathPow = Math.pow; var maxWord = mathPow(2, 32); var lengthProperty = 'length' var i, j; var result = '' var words = []; var asciiBitLength = ascii[lengthProperty]*8; var hash = sha256.h = sha256.h || []; var k = sha256.k = sha256.k || []; var primeCounter = k[lengthProperty]; var isComposite = {}; for (var candidate = 2; primeCounter < 64; candidate++) { if (!isComposite[candidate]) { for (i = 0; i < 313; i += candidate) { isComposite[i] = candidate; } hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0; k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0; } } ascii += '\x80' while (ascii[lengthProperty]%64 - 56) ascii += '\x00' for (i = 0; i < ascii[lengthProperty]; i++) { j = ascii.charCodeAt(i); if (j>>8) return; words[i>>2] |= j << ((3 - i)%4)*8; } words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0); words[words[lengthProperty]] = (asciiBitLength) for (j = 0; j < words[lengthProperty];) { var w = words.slice(j, j += 16); var oldHash = hash; hash = hash.slice(0, 8); for (i = 0; i < 64; i++) { var i2 = i + j; var w15 = w[i - 15], w2 = w[i - 2]; var a = hash[0], e = hash[4]; var temp1 = hash[7] + (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) + ((e&hash[5])^((~e)&hash[6])) // ch + k[i] // Expand the message schedule if needed + (w[i] = (i < 16) ? w[i] : ( w[i - 16] + (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) + w[i - 7] + (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) )|0 ); var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) + ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj hash = [(temp1 + temp2)|0].concat(hash); hash[4] = (hash[4] + temp1)|0; } for (i = 0; i < 8; i++) { hash[i] = (hash[i] + oldHash[i])|0; } } for (i = 0; i < 8; i++) { for (j = 3; j + 1; j--) { var b = (hash[i]>>(j*8))&255; result += ((b < 16) ? 0 : '') + b.toString(16); } } return result; }; // Test blockchain = new Blockchain(); blockchain.addBlock("Bob loves Alice"); blockchain.addBlock("Bob sends Alice USD 100"); blockchain.addBlock("Alice loves Bob"); console.log(JSON.stringify(blockchain, "", " ")); console.log("Is valid: " + blockchain.isValid());
0
1 комментарий
Света Воробьева

Спасибо «Кривой» за прямой бонус в 25%. А, уже 30%))

Ответить
Развернуть ветку
Читать все 1 комментарий
null