1Создание блокчейна в 60 строк Javascript
3Создайте сеть p2p и выпустите свою криптовалюту
В последние годы криптовалюты и блокчейны стали двумя развивающимися областями, поэтому сегодня я поделюсь своим способом создания блокчейна на Javascript всего в 60 строках кода.
Прежде чем приступать к кодированию, нам нужно понять, что такое блокчейн. Технически блок-цепочка в своем минимальном виде — это просто список, содержащий объекты, которые имеют некоторую базовую информацию, такую как временная метка, транзакции, хеш и т. д. Его данные должны быть неизменяемыми и неподдающимися взлому. Современные платформы, такие как Ethereum, Cardano, Polkadot… имеют гораздо более сложные вещи, но в этой статье мы останемся простыми.
Node.js использовался для этого проекта, поэтому обязательно установите его, если вы еще этого не сделали.
На протяжении всей статьи будет использоваться объектно-ориентированный стиль программирования, поэтому необходимо обладать базовыми знаниями.
Как уже говорилось, блок — это просто объект, содержащий некоторую информацию о нем, поэтому у нас должен быть такой Block
класс:
1 2 3 4 5 6 7 8 9 10 |
class Block { constructor(timestamp = "", data = []) { this.timestamp = timestamp; // this.data should contain information like transactions. this.data = data; } } |
Итак, у нас есть timestamp
и data
, но блокчейну нужна неизменность. Мы можем добиться этого эффекта, используя функцию хеширования, которая хэширует все наши свойства в блоке. Предлагаем прочитать о хейтинге в Википедии, он играет важную роль в блокчейне. По сути, он принимает сообщение и выводит «хешированное» сообщение фиксированной длины, небольшое изменение сообщения сделает вывод совершенно другим.
Использовался sha256
алгоритм. Чтобы реализовать функцию хеширования, нужно воспользоваться встроенным crypto
пакетом Nodejs:
1 2 3 4 |
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex"); |
Приведенный выше код должен дать нам то, что мы хотели, но если вы хотите узнать, как это работает, ознакомьтесь с официальным документом Node.js о хэш-классе .
У нас должно получиться что-то вроде этого:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Get the sha256 hash function. const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex"); class Block { constructor(timestamp = "", data = []) { this.timestamp = timestamp; this.data = data; this.hash = this.getHash(); this.prevHash = ""; // previous block's hash } // Our hash function. getHash() { return SHA256(this.prevHash + this.timestamp + JSON.stringify(this.data)); } } |
Потому что каждый раз, когда что-то изменяется, SHA256 выбрасывает что-то совершенно другое, так что это может частично гарантировать неизменность.
Это prevHash
свойство также играет большую роль в неизменности, оно гарантирует, что блоки останутся неизменными на протяжении всего срока службы блокчейна. Он содержит хэш предыдущего блока, поэтому вы можете гарантировать неизменность этого предыдущего блока, поскольку небольшое изменение приведет к тому, что текущий блок getHash
будет другим. Вы можете видеть, что он пуст, но мы кое-что с ним сделаем позже в этой статье.
Давайте перейдем к классу блокчейн.
Как уже говорилось, блокчейн — это список блоков, поэтому у нас может быть такая базовая форма:
1 2 3 4 5 6 7 8 9 |
class Blockchain { constructor() { // This property will contain all the blocks. this.chain = []; } } |
У вас должен быть блок генезиса, который технически является только первым блоком:
1 2 3 4 5 6 7 8 9 |
class Blockchain { constructor() { // Create our genesis block this.chain = [new Block(Date.now().toString())]; } } |
Просто для удобства создается функцию для получения последнего блока:
1 2 3 4 5 6 |
getLastBlock() { return this.chain[this.chain.length - 1]; } |
Теперь у нас должен быть способ добавить блок в блокчейн.
1 2 3 4 5 6 7 8 9 10 11 12 |
addBlock(block) { // Since we are adding a new block, prevHash will be the hash of the old latest block block.prevHash = this.getLastBlock().hash; // Since now prevHash has a value, we must reset the block's hash block.hash = block.getHash(); // Object.freeze ensures immutability in our code this.chain.push(Object.freeze(block)); } |
Нам нужно знать, действительна ли цепочка или нет, поэтому нам нужен метод для проверки проверки. Цепочка действительна, если хэш блока равен тому, что возвращает его метод хеширования, а prevHash
свойство блока должно быть равно хешу предыдущего блока.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
isValid(blockchain = this) { // Iterate over the chain, we need to set i to 1 because there are nothing before the genesis block, so we start at the second block. for (let i = 1; i < blockchain.chain.length; i++) { const currentBlock = blockchain.chain[i]; const prevBlock = blockchain.chain[i-1]; // Check validation if (currentBlock.hash !== currentBlock.getHash() || prevBlock.hash !== currentBlock.prevHash) { return false; } } return true; } |
Этот метод будет играть действительно важную роль, когда наш блокчейн будет работать в сети p2p.
В одноранговой сети, где нет сторонней системы для одобрения действий людей, без какого-либо механизма консенсуса, узлы (просто говоря, люди) согласятся с большинством, но люди могут стать злоумышленниками и взять под свой контроль большинства, поэтому нам нужен механизм консенсуса. Механизм консенсуса существует не только для того, чтобы остановить атаки, он существует для того, чтобы люди не были злоумышленниками. Доказательство работы является одним из них.
Прежде чем мы продолжим, система работает, заставляя вас увеличивать значение, называемое nonce, чтобы получить хеш, который начинается с количества нулей, равного/относящегося к сложности.
PoW может помочь с двумя вещами: он предотвращает злоумышленников, потому что почти невозможно догнать другие узлы в одиночку, и он обеспечивает вознаграждение за майнинг, чтобы люди пытались быть нейтральными, а не быть атакующими.
Мы можем реализовать систему PoW, добавив в наш блок mine
метод и nonce
свойство:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Block { constructor(timestamp = "", data = []) { this.timestamp = timestamp; this.data = data; this.hash = this.getHash(); this.prevHash = ""; // previous block's hash this.nonce = 0; } // Our hash function. getHash() { return SHA256(this.prevHash + this.timestamp + JSON.stringify(this.data) + this.nonce); } mine(difficulty) { // Basically, it loops until our hash starts with // the string 0...000 with length of <difficulty>. while (!this.hash.startsWith(Array(difficulty + 1).join("0"))) { // We increases our nonce so that we can get a whole different hash. this.nonce++; // Update our new hash with the new nonce value. this.hash = this.getHash(); } } } |
Потому что, когда мы изменим небольшую деталь в нашем блоке, хэш будет совсем другим, поэтому мы просто увеличиваем нонс раз за разом, пока хеш не совпадет с тем, который нам нужен.
(Обратите внимание, что Биткойн и другие обычно используют другой способ проверки сложности, но мы остаемся простыми)
Переходя к Blockchain
классу, мы должны создать свойство сложности:
1 2 3 4 |
this.difficulty = 1; |
Устанавливаем его на 1, сложность должна обновляться в зависимости от количества добытых блоков.
Мы также должны обновить addBlock
метод из Блокчейна:
1 2 3 4 5 6 7 8 9 |
addBlock(block) { block.prevHash = this.getLastBlock().hash; block.hash = block.getHash(); block.mine(this.difficulty); this.chain.push(Object.freeze(block)); } |
Теперь все блоки должны быть добыты перед добавлением в цепочку.
Поскольку мы остаемся простыми, я использовал систему проверки работоспособности для этого блокчейна. Обратите внимание, что в большинстве современных блокчейнов используется более совершенная система, называемая доказательством доли (или многие из ее модернизированных вариаций).
Создайте новый файл, этот файл будет файлом входа.
Давайте использовать наш только что созданный блокчейн! Я пока позвоню JeChain
.
Сначала экспортируйте необходимые классы:
1 2 3 4 |
module.exports = { Block, Blockchain }; |
1 2 3 4 5 6 7 8 9 10 11 12 |
const { Block, Blockchain } = require("./your-blockchain-file.js"); const JeChain = new Blockchain(); // Add a new block JeChain.addBlock(new Block(Date.now().toString(), { from: "John", to: "Bob", amount: 100 })); // (This is just a fun example, real cryptocurrencies often have some more steps to implement). // Prints out the updated chain console.log(JeChain.chain); |
Это должно выглядеть так:
Первый блок — это наш блок генезиса, второй блок — добавленный блок.
Время блока — это постоянное значение, напоминающее предполагаемое время добавления блока в цепочку. Платформы, такие как биткойн, имеют время блокировки 10 минут, а время блокировки Ethereum — 13 секунд.
В случае с Биткойном его сложность обновляется каждые 2016 блоков, которые были добыты. Он использует эту формулу для расчета новой сложности:
1 2 3 4 |
old difficulty * (2016 blocks * 10 minutes) / mining time for the previous 2016 blocks |
Теперь давайте кодировать!
Во-первых, у нас должно быть время блока, я просто установлю его на 30 секунд, что равно 30000 мс. Я использую миллисекунду, потому что она лучше работает с Date.now()
.
1 2 3 4 |
this.blockTime = 30000; |
(Обратите внимание, что мы кодируем в Blockchain
классе здесь).
Просто в качестве примера я создам свою собственную систему: сложность будет увеличиваться на 1, если время блока меньше, чем фактическое время добычи блока, в противном случае она будет уменьшаться.
1 2 3 4 5 6 7 8 9 10 11 |
addBlock(block) { block.prevHash = this.getLastBlock().hash; block.hash = block.getHash(); block.mine(this.difficulty); this.chain.push(Object.freeze(block)); this.difficulty += Date.now() - parseInt(this.getLastBlock().timestamp) < this.blockTime ? 1 : -1; } |
Из-за того, как мы проверяли сложность ранее, это должно работать нормально. Тем не менее, лучше проверять сложность с помощью log16(difficulty)
, а не саму сложность, и, сделав это, теперь вы можете использовать формулу сложности Биткойн.
Но вы можете придумать свою формулу. Вы должны подумать, что лучше для безопасности, но при этом иметь хорошую производительность.
Вы можете получить полный исходный код в этом репозитории:
Много можно узнать о блокчейнах из « Просто объяснено» . Также можете посмотреть видео на Youtube, там действительно хорошая серия обучающих материалов по блокчейну.
Источник статьи: http://dev.to/