среда, 4 апреля 2018 г.

Blockchain для самых маленьких

Доброго времени суток!
Blockchain! Он начал свое победоносное шествие по планете! Проник в мозг бизнесменов, политиков, дворников!
Сам Герман Греф - рассказывает нам о том, как мы заживем с этой чудной технологией!
Поскольку бежать и закупать на последнюю зарплату пригоршню биткоинов уже поздно - расскажу как это устроено, чтобы вы могли сделать свои биткоины - с блэкджеком - и транзакциями=)

Предполагается, что читатель знаком с языком программирования Java.



Историческая справка
Blockchain - как технология - исторически неразрывно связан с понятием Bitcoin, поэтому придется - несмотря на то, что на фоне Грефа я смотрюсь недостаточно убедительно - сначала поговорить о биткоинах.

Итак, что нам известно?
Некто, называющий себя Сатоши Накамото в 2008 году опубликовал Bitcoin Whitepaper - документ, в котором описываются принципы работы биткоина. 3 января 2009 года был сгенерирован первый блок этой платежной системы - и все заверте...
Что интересно - реальная личность Сатоши не раскрыта до сих пор, так что за этим псевдонимом может прятаться кто угодно и в любом количестве. Версий много - ЦРУ, ФСБ, рептилоиды. Пока не доказано обратного - для простоты будем считать, что биткоины создал я=)


Что более интересно - в биткоине используется интересный способ хранения данных в определенном формате в виде цепочек блоков - блокчейна - о котором дальше и пойдет речь

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

Или, в более наглядном виде, блокчейн это:


Самый первый блок - паровозик - зовется genesis-блоком. Его хэш является точкой отсчета блокчейна.

Что такое хэш?
Хэш - это необратимая функция данных. При изменении данных - хэш будет изменяться. Этот простой механизм позволяет отслеживать валидность блокчейна. Если кто-либо попытается изменить данные в блокчейне - блокчейн станет невалидным, так как хэши в блоках и вновь вычисленные хэши данных блоков будут не совпадать. 
Таким образом, чтобы сохранить валидность блокчейна - необходимо заново перехэшировать данные всех блоков.

Чтобы избежать этой ситуации, системы, основанные на базе блокчейн, используют дополнительные меры верификации. Например в сети Bitcoin запись блока должна быть подтверждена другими участниками сети. Хотя теоретически - возможность обойти это остается

Реализация
Итак, напишем простую реализацию, которая демонстрирует описанную выше концепцию. Для начала - создадим класс, представляющий собой блок:

package com.blogspot.toolkas.blockchain;

public class Block {
    /**
     * Данные блока.
     */
    private final byte[] data;
    /**
     * Хэш блока.
     */
    private final byte[] hash;
    /**
     * Хэш предыдущего блока.
     */
    private final byte[] prevHash;

    /**
     * Время создания блока.
     */
    private final long created;

    public Block(byte[] data, byte[] hash, byte[] prevHash, long created) {
        this.data = data;
        this.hash = hash;
        this.prevHash = prevHash;
        this.created = created;
    }

    public byte[] getData() {
        return data;
    }

    public byte[] getHash() {
        return hash;
    }

    public byte[] getPrevHash() {
        return prevHash;
    }

    public long getCreated() {
        return created;
    }
}
Создадим класс BlockChain, который управляет блоками:
package com.blogspot.toolkas.blockchain;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BlockChain {
    private final List blocks = new ArrayList();

    public BlockChain() {
        blocks.add(new Block(null, new byte[]{1, 2, 3}, null));
    }
}

При инициализации блокчейна - автоматически создается и добавляется genesis-блок без данных и с произвольно выбранным хэшем.

Для хэширования данных добавим в класс BlockChain методы hash:

    private byte[] hash(Block block) throws NoSuchAlgorithmException {
        return hash(block.getData());
    }

    private byte[] hash(byte[] data) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        return digest.digest(data);
    }

Теперь добавим метод, проверяющий валидность блокчейна. Алгоритм проверки в нашем случае довольно прост. Для каждого блока необходимо:
  1. Проверить на совпадение хэш предыдущего блока со значением prevHash;
  2. Вычислить хэш данных каждого блока и проверить его на совпадение с переменной hash;
Если для какого либо блока одна из проверок провалилась - блок считается невалидным. Таким образом, добавим метод checkValidity, который реализует описанный выше алгоритм:

    public boolean checkValidity() throws NoSuchAlgorithmException {
        for (int i = 1; i < blocks.size(); i++) {
            Block prev = blocks.get(i - 1);
            Block block = blocks.get(i);

            byte[] newHash = hash(block);
            if (!Arrays.equals(newHash, block.getHash())) {
                return false;
            }

            if (!Arrays.equals(prev.getHash(), block.getPrevHash())) {
                return false;
            }
        }
        return true;
    }

Понятно, что любое изменение данных в блоках приведет к тому, что метод checkValidity вернет false.

Реализуем последний метод - добавления новых блоков в цепочку. Данный метод будет хэшировать данные блока и его время создания, корректно инициализировать блок - и добавлять в цепочку. Время рождения блока тут используется для того, чтобы независимо от данных сделать хэш уникальным:

    public void add(byte[] data) throws IOException, 
                               NoSuchAlgorithmException {
        Objects.requireNonNull(data);

        long now = System.currentTimeMillis();
        byte[] fullData = getFullData(data, now);
        byte[] hash = hash(fullData);

        Block last = blocks.get(blocks.size() - 1);
        Block block = new Block(fullData, hash, 
                          last.getHash(), now);
        blocks.add(block);
    }

    private byte[] getFullData(byte[] data, long now) 
                                               throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try(DataOutputStream output = new DataOutputStream(baos)) {
            output.write(data);
            output.writeLong(now);
        }

        return baos.toByteArray();
    }
Проверка
Для того, чтобы быстро проверить корректность работы нашей реализации
алгоритма blockchain, выполним простой код:
        BlockChain blockChain = new BlockChain();
        blockChain.add("AAA".getBytes("UTF-8"));
        blockChain.add("BBB".getBytes("UTF-8"));
        blockChain.add("CCC".getBytes("UTF-8"));
        System.out.println(blockChain.checkValidity());
Результатом работы приведенного кода будет true. Если же вы перед вызовом метода checkValidity() средствами IDE поменяете в одном из блоков (кроме первого) данные, то результатом вызова метода checkValidity() будет false.

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



Комментариев нет:

Отправить комментарий