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

С технической точки зрения, блокчейн – это распределенная база данных со специальными функциями, такими как проверка, хранение и синхронизация данных. С точки зрения бизнеса, это концепция, которая позволяет проводить эффективные и надежные одноранговые транзакции, не требуя центральной точки контроля. Впрочем, существуют некоторые отличия блокчейна по сравнению с распределенной базой данных. В Blockchain можно только добавить информацию, затем данные проверяются и подписываются. Но как же создать блокчейн? В данном материале мы рассмотрим как запрограммировать свой блокчейн примерно в 100 строках кода, используя язык программирования javascript.
Программная архитектура блокчейна
Blockchain – это цепочка блоков. Чтобы было все как можно проще, но не слишком простым, каждый блок включает в себя наиболее необходимые элементы: индекс, временную метку, данные, хэш и предыдущий хэш. И каждый новый блок, который должен быть добавлен в цепочку, также должен быть проверен.
import CryptoJS from 'crypto-js';
import chain from './chain';
export const calcHash = ({index, prevHash, timestamp, data}) => {
return CryptoJS.SHA256(index + prevHash + timestamp + data).toString();
}
export const create = (data) => {
const prev = chain.last();
const index = prev.index + 1;
const timestamp = new Date().getTime();
const prevHash = prev.hash;
const hash = calcHash({ index, prevHash, timestamp, data });
return { index, timestamp, data, prevHash, hash };
};
export const isNewBlockValid = (newBlock, prevBlock = chain.last()) => {
let isValid = true;
if (prevBlock.index + 1 !== newBlock.index) {
console.log('New block has invalid index');
isValid = false;
} else if (prevBlock.hash !== newBlock.prevHash) {
console.log('New block has invalid prevHash');
isValid = false;
} else if (calcHash(newBlock) !== newBlock.hash) {
console.log('New block has invalid hash');
isValid = false;
}
return isValid;
};
Цепочка блоков – это одноэлементная реализация, которую вы разрешаете расширять с помощью нового проверенного блока и заменяете цепочку в случае соединения нескольких одноранговых узлов, выбирая ту, которая имеет самое длинное количество блоков в ней.
import { calcHash, isNewBlockValid } from './block';
const Chain = (function () {
let instance;
const origin = {
index: 0,
timestamp: 0,
data: 'Hello Blockchain!',
prevHash: 0,
hash: calcHash({ index: 0, prevHash: 0, timestamp: 0, data: 'Hello Blockchain!' })
};
const chain = [origin];
function isChainValid(newChain) {
let isValid = true;
if (JSON.stringify(newChain[0]) !== JSON.stringify(origin)) {
console.log('Received chain is invalid. Origin block does not coincide');
isValid = false;
return isValid;
}
const tempChain = [newChain[0]];
for (let i = 1; i < newChain.length; i += 1) {
if (isNewBlockValid(newChain[i], tempChain[i - 1])) {
tempChain.push(newChain[i]);
} else {
isValid = false;
return isValid;
}
}
return isValid;
}
function get() {
return chain;
}
function update(block) {
if (isNewBlockValid(block)) {
chain.push(block);
}
}
function last() {
return chain.slice().pop();
}
function replace(newChain) {
if (isChainValid(newChain) && newChain.length > chain.length) {
console.log('Received chain is valid. Replacing current chain with received chain');
chain.length = 0; // clear current chain
chain.push(...newChain); // fill current already empty chain with the new one if valid
} else {
console.log('Received chain is invalid');
}
}
function create() {
return { get, update, last, replace };
}
return {
init() {
if (!instance) {
instance = create();
}
return instance;
}
};
}());
export default Chain.init();
Существенной особенностью узла является совместное использование и синхронизация цепочки с другими узлами. Когда узел генерирует новый блок, он передает его в сеть. Когда узел подключается к новому одноранговому узлу, он запрашивает последний блок. И когда узел встречает блок с индексом, большим, чем текущий известный блок, он либо добавляет блок своей текущей цепочке, либо запросы для полной цепочки блоков. Нет автоматического обнаружения сверстников. Местоположение (URL-адресов) одноранговых узлов добавляется вручную.
import express from 'express';
import chain from '../chain';
import { create } from '../block';
import sockets from '../p2p/sockets';
import { connectToPeers } from '../p2p/index';
import { broadcast } from '../p2p/handlers';
import { responseLatestMsg } from '../p2p/actions';
const router = express.Router();
router.get('/health-check', (req, res) => res.send('OK'));
router.get('/chain', (req, res) => {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(chain.get()))
});
router.post('/mine', (req, res) => {
const block = create(req.body.data);
chain.update(block);
broadcast(responseLatestMsg());
console.log('New block in chain has been added: ', block);
res.send(block);
});
router.get('/peers', (req, res) => {
res.send(sockets.get().map(s => `${s._socket.remoteAddress}:${s._socket.remotePort}`));
});
router.post('/connect', (req, res) => {
const { peer } = req.body;
connectToPeers([peer]);
console.log('New peer in p2p websocket has been added: ', peer);
res.send(peer);
});
export default router;
Пользователь может управлять узлом с помощью HTTP-сервера, чтобы отображать все блоки в цепочке, создавать новый с содержимым, заданным пользователем, и добавлять одноранговые узлы. Следует отметить, что узел предоставляет два веб-сервера: один для пользователя для qp-управления узлом (HTTP-сервером) и один для одноранговой связи между узлами (сервер Websocket P2P). Дополнительную информацию по реализации такого блокчейна можно найти на github.com/stremann/chainpro.