Подписывайся на Telegram-канал

Разбираемся с блокчейн

Разбираемся с блокчейн

https://t.me/it_programmist

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

Кому как не нам, программистам, положено разбираться в этом.

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

Блокчейн - это база данных, которая хранится на множестве устройств и поддерживает постоянно растущий организованный список записей. Так как база хранится ни в одном месте, в во множестве, то она называется распределённой или децентрализованной. Называется это всё блокчейн, потому что это по сути цепочка блоков(chain, block - англ.). Если посмотреть на простую цепь (якорную или от старых советских сливных бачков), то можно заметить, что каждое звено связано с предыдущим. В блокчейне взят за основу этот принцип.

Давайте попробуем реализовать простейший пример популярной сейчас технологии блокчейн на самом используемом языке - JavaScript.


Блок

В нашем простейшем случае блок будет состоять из id(обзовём его index и будем просто инкриминировать(увеличивать на единицу) с нуля), временной метки(timstamp), данных(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();
}
}


Хеш

Каждый блок имеет свой хэш и хэш предыдущего блока. Как выше было написано, хеширование блоков нужно для сохранения целостности данных. Хеш будем создавать с помощью алгоритма SHA-256. Так как у нас простая реализация для понимания основ, нам не важно, что этот вид хеша не имеет отношения к майнингу, так как мы в данном случае не реализуем защиту с помощью доказательства выполнения работы.


var calculateHash = (index, previousHash, timestamp, data) => {
return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};


Функция генерации блока

Для генерации блока необходимо знать хэш предыдущего блока. Сформируем остальные данные нашей структуры (index, hash и timestamp). Данные (blockData) - это, по сути, то, что хранит наша база, и что сюда поместить определятся пользователем и приходит извне.

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('Принятый блокчейн не походит);
}
};


Связь с другими узлами

Чтобы это всё работало должна существовать связь между узлами. Для синхронизации сети существуют следующие правила:

  • Когда узел генерирует новый блок, он сообщает об этом в сеть;
  • Когда узел подсоединяется к новой одноранговой сети(пиру/peer), он запрашивает информацию о последнем сгенерированном блоке;
  • Когда узел сталкивается с блоком, у которого индекс больше, чем у него, он либо добавляет блок к своей цепи, либо запрашивает информацию о полной цепи.

Автоматический поиск пиров не осуществляется, все ссылки добавляются в ручную.


Контроль над узлом

Пользователь, в некотором роде, должен иметь возможность контролировать узел. Это делается путем настройки http-сервера.


var initHttpServer = () => {
var app = express();
app.use(bodyParser.json());

app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));
app.post('/mineBlock', (req, res) => {
var newBlock = generateNextBlock(req.body.data);
addBlock(newBlock);
broadcast(responseLatestMsg());
console.log('block added: ' + JSON.stringify(newBlock));
res.send();
});
app.get('/peers', (req, res) => {
res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));
});
app.post('/addPeer', (req, res) => {
connectToPeers([req.body.peer]);
res.send();
});
app.listen(http_port, () => console.log('Listening http on port: ' + http_port));
};


Как видно, пользователь может взаимодействовать с узлом следующими способами:

  • Просматривать список всех блоков
  • Создавать новый блок с содержанием, заданным пользователем
  • Просматривать или добавлять одноранговых пользователей


Управление узлом с помощью Curl:

#получить все блоки из узла
curl http://localhost:3001/blocks


Архитектура

Следует отметить, что узел фактически предоставляет два веб-сервера: один для пользователя, чтобы контролировать узел (http-сервер) и один для одноранговой (peer-to-peer) связи между узлами.(websocket сервер http)

--------

Это простейшая реализация блокчейн на NodeJS для общего понимания и ознакомления с основами. До тех пор, пока здесь нет «майнинг» алгоритма для (PoS of PoW), мы не можем использовать эту реализацию в общедоступной сети. Тем не менее здесь реализованы основные функции для реализации блокчейн.


___________

Я - программист!

Шутеечки в Дневнике программиста

Чат Клуб программистов

Ещё