- Регистрация
- 9 Май 2015
- Сообщения
- 1,071
- Баллы
- 155
- Возраст
- 51
Понять концепцию (англ. block chain, цепочка блоков транзакций) довольно просто: это распределенная (т.е. размещенная не на одном компьютере, а на различных устройствах компьютерной сети) база данных, которая поддерживает постоянно растущий организованный список записей. Но также просто спутать блокчейн с задачами, которые мы пытаемся с помощью него решить — на данный момент в сознании людей термин довольно прочно связан с концепциями транзакций, или .
Только вот блокчейн — это не то же самое, что биткоин, и понять основы работы цепочки блоков намного проще, чем кажется, особенно в контексте исходного кода, на котором она строится. В этой статье предлагается построить простую модель с помощью 200 строк кода на JavaScript. Исходный код проекта, который мы назовем NaiveChain, вы можете найти на . Сразу оговорим, что этот материал создан исключительно в обучающих целях, рассматривает лишь основы построения таких цепочек и является своеобразной песочницей — здесь вообще не упоминается , например.
Мы будем использовать стандарт ECMAScript 6. Если вам нужно освежить в памяти его особенности, воспользуйтесь нашей шпаргалкой: и .
Структура блока
Первый шаг — определить элементы, которые должен содержать блок. Для простоты включим только самое необходимое: индекс (index), временную метку (timestamp), данные (data), хеш и хеш предыдущего блока, который нужно записывать, чтобы сохранить структурную целостность цепи.
class Block {
constructor(index, previousHash, timestamp, data, hash) {
this.index = index;
this.previousHash = previousHash.toString();
this.timestamp = timestamp;
this.data = data;
this.hash = hash.toString();
}
}
Хеш блока
Хеширование блоков нужно для сохранения целостности данных. В нашем примере для этого применяется алгоритм . Этот вид хеша не имеет отношения к майнингу, так как мы в данном случае не реализуем защиту с помощью .
var calculateHash = (index, previousHash, timestamp, data) => {
return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};
Генерируем блок
Для генерации блока нам нужно знать хеш предыдущего блока и внести остальные элементы, которые мы обозначили в структуре блока. Данные предоставляются конечным пользователем.
var generateNextBlock = (blockData) => {
var previousBlock = getLatestBlock();
var nextIndex = previousBlock.index + 1;
var nextTimestamp = new Date().getTime() / 1000;
var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);
return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);
};
Хранение блоков
Для хранения блокчейна используем массив. Первый блок всегда является жестко заданным «генезис-блоком».
var getGenesisBlock = () => {
return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");
};
var blockchain = [getGenesisBlock()];
Подтверждаем целостность блоков
Мы всегда должны иметь возможность подтвердить целостность блока или цепи. Особенно когда получаем новые блоки с других узлов и должны решить, принимать их или нет.
var isValidNewBlock = (newBlock, previousBlock) => {
if (previousBlock.index + 1 !== newBlock.index) {
console.log('неверный индекс');
return false;
} else if (previousBlock.hash !== newBlock.previousHash) {
console.log('неверный хеш предыдущего блока');
return false;
} else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
console.log('неверный хеш: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash);
return false;
}
return true;
};
Выбираем самую длинную цепь
Последовательность блоков в цепи всегда должна быть задана явно, поэтому в случае конфликтов (например, на двух узлах одновременно сгенерированы блоки под одним и тем же номером) мы выбираем цепь, в которой содержится большее количество блоков.
var replaceChain = (newBlocks) => {
if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
console.log('Принятый блокчейн является валидным. Происходит замена текущего блокчейна на принятый');
blockchain = newBlocks;
broadcast(responseLatestMsg());
} else {
console.log('Принятый блокчейн не является валидным');
}
};
Сообщение с другими узлами сети
Неотъемлемая часть узла — обмен данными с другими узлами. Следующие правила используются для поддержания синхронизации в сети:
Автоматический поиск пиров не осуществляется, все ссылки добавляются в ручную.
Контроль за узлом
Пользователь должен иметь возможность каким-то образом контролировать узел, что решается с помощью установки сервера HTTP. При взаимодействии с узлом доступны следующие функции:
Самый прямой путь взаимодействия — с помощью :
# вывести список всех блоков на узле
curl http://localhost:3001/blocks
Архитектура
Стоит заметить, что узел обращается к двум веб серверам: к HTTP для пользовательского контроля за узлом и к Websocket HTTP для установки соединения P2P между узлами.
Готово! Мы реализовали простой небольшой блокчейн. Со всеми деталями проекта можно в репозитории автора на GitHub.
— .
Только вот блокчейн — это не то же самое, что биткоин, и понять основы работы цепочки блоков намного проще, чем кажется, особенно в контексте исходного кода, на котором она строится. В этой статье предлагается построить простую модель с помощью 200 строк кода на JavaScript. Исходный код проекта, который мы назовем NaiveChain, вы можете найти на . Сразу оговорим, что этот материал создан исключительно в обучающих целях, рассматривает лишь основы построения таких цепочек и является своеобразной песочницей — здесь вообще не упоминается , например.
Мы будем использовать стандарт ECMAScript 6. Если вам нужно освежить в памяти его особенности, воспользуйтесь нашей шпаргалкой: и .
Структура блока
Первый шаг — определить элементы, которые должен содержать блок. Для простоты включим только самое необходимое: индекс (index), временную метку (timestamp), данные (data), хеш и хеш предыдущего блока, который нужно записывать, чтобы сохранить структурную целостность цепи.
class Block {
constructor(index, previousHash, timestamp, data, hash) {
this.index = index;
this.previousHash = previousHash.toString();
this.timestamp = timestamp;
this.data = data;
this.hash = hash.toString();
}
}
Хеш блока
Хеширование блоков нужно для сохранения целостности данных. В нашем примере для этого применяется алгоритм . Этот вид хеша не имеет отношения к майнингу, так как мы в данном случае не реализуем защиту с помощью .
var calculateHash = (index, previousHash, timestamp, data) => {
return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};
Генерируем блок
Для генерации блока нам нужно знать хеш предыдущего блока и внести остальные элементы, которые мы обозначили в структуре блока. Данные предоставляются конечным пользователем.
var generateNextBlock = (blockData) => {
var previousBlock = getLatestBlock();
var nextIndex = previousBlock.index + 1;
var nextTimestamp = new Date().getTime() / 1000;
var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);
return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);
};
Хранение блоков
Для хранения блокчейна используем массив. Первый блок всегда является жестко заданным «генезис-блоком».
var getGenesisBlock = () => {
return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");
};
var blockchain = [getGenesisBlock()];
Подтверждаем целостность блоков
Мы всегда должны иметь возможность подтвердить целостность блока или цепи. Особенно когда получаем новые блоки с других узлов и должны решить, принимать их или нет.
var isValidNewBlock = (newBlock, previousBlock) => {
if (previousBlock.index + 1 !== newBlock.index) {
console.log('неверный индекс');
return false;
} else if (previousBlock.hash !== newBlock.previousHash) {
console.log('неверный хеш предыдущего блока');
return false;
} else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
console.log('неверный хеш: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash);
return false;
}
return true;
};
Выбираем самую длинную цепь
Последовательность блоков в цепи всегда должна быть задана явно, поэтому в случае конфликтов (например, на двух узлах одновременно сгенерированы блоки под одним и тем же номером) мы выбираем цепь, в которой содержится большее количество блоков.
var replaceChain = (newBlocks) => {
if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
console.log('Принятый блокчейн является валидным. Происходит замена текущего блокчейна на принятый');
blockchain = newBlocks;
broadcast(responseLatestMsg());
} else {
console.log('Принятый блокчейн не является валидным');
}
};
Сообщение с другими узлами сети
Неотъемлемая часть узла — обмен данными с другими узлами. Следующие правила используются для поддержания синхронизации в сети:
- Когда узел генерирует новый блок, он сообщает об этом в сеть;
- Когда узел подсоединяется к новому пиру, он запрашивает информацию о последнем сгенерированном блоке;
- Когда узел сталкивается с блоком, у которого индекс больше, чем у него, он либо добавляет блок к своей цепи, либо запрашивает информацию о полной цепи.
Автоматический поиск пиров не осуществляется, все ссылки добавляются в ручную.
Контроль за узлом
Пользователь должен иметь возможность каким-то образом контролировать узел, что решается с помощью установки сервера HTTP. При взаимодействии с узлом доступны следующие функции:
- Вывести список всех блоков;
- Создать новый блок с пользовательским контентом;
- Вывести списком или добавить пиры.
Самый прямой путь взаимодействия — с помощью :
# вывести список всех блоков на узле
curl http://localhost:3001/blocks
Архитектура
Стоит заметить, что узел обращается к двум веб серверам: к HTTP для пользовательского контроля за узлом и к Websocket HTTP для установки соединения P2P между узлами.
Готово! Мы реализовали простой небольшой блокчейн. Со всеми деталями проекта можно в репозитории автора на GitHub.
— .