Хабрахабр

[Перевод] Blockchain на Go. Часть 1: Прототип

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

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

Блок

Начнем с "блока" части "блокчейна". В блокчейне, блоки хранят полезную информацию. Например, в bitcoin блоки хранят транзакции, суть любой криптовалюты. Помимо полезной информации, в блоке содержится служебная информация: версия, дата создания в виде timestamp и хеш предыдущего блока. В этой статье мы будем создавать упрощенный блок, содержащий только существенную информацию. Опишем его в виде go структуры.

type Block struct { Timestamp int64 Data []byte PrevBlockHash []byte Hash []byte
}

Timestamp это время создания блока, Data это полезная информация, содержащаяся в блоке, PrevBlockHash хранит хэш предыдущего блока, и наконец, Hash содержит хэш блока. В спецификации bitcoin Timestamp, PrevBlockHash и Hash образуют заголовок блока и образуют отдельную от транзакций (в нашем случае это Data) структуру. Мы же смешали их для простоты.

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

Обсудим это в следующей статье, а сейчас мы просто соеденим все поля блока и захэшируем результат в SHA-256:

func (b *Block) SetHash() { timestamp := []byte(strconv.FormatInt(b.Timestamp, 10)) headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{}) hash := sha256.Sum256(headers) b.Hash = hash[:]
}

Далее, создадим "конструктор" для нашего блока

func NewBlock(data string, prevBlockHash []byte) *Block { block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}} block.SetHash() return block
}

Блок готов!

Блокчейн

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

В Golang эта структура может быть реализована с помощью массива (array) и карты (map): массив будет содержать упорядоченный список хэшей (массивы упорядоченны в Go), а карты будут хранить пары hash -> block (карты не упорядочены). Но для нашего прототипа, мы будем использовать только массивы потому, что нам не требуется получать блок по его хэшу.

type Blockchain struct { blocks []*Block
}

Это наш первый блокчейн! Никогда не думал, что это так просто 🙂

Добавим возможность добавлять блоки в него.

func (bc *Blockchain) AddBlock(data string) { prevBlock := bc.blocks[len(bc.blocks)-1] newBlock := NewBlock(data, prevBlock.Hash) bc.blocks = append(bc.blocks, newBlock)
}

Чтобы добавить первый блок, нам нужен существующий, но наш блокчейн пуст! Таким образом нам в любом блокчейне должен быть как минимум один блок, который называют genesis блоком. Реализуем метод, создающий такой блок.

func NewGenesisBlock() *Block { return NewBlock("Genesis Block", []byte{})
}

А теперь реализуем функцию создающую блокчейн с genesis блоком.

func NewBlockchain() *Blockchain { return &Blockchain{[]*Block{NewGenesisBlock()}}
}

Давайте проверим, что блокчейн работает корректно

func main() { bc := NewBlockchain() bc.AddBlock("Send 1 BTC to Ivan") bc.AddBlock("Send 2 more BTC to Ivan") for _, block := range bc.blocks { fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash) fmt.Printf("Data: %s\n", block.Data) fmt.Printf("Hash: %x\n", block.Hash) fmt.Println() }
}

Этот код выведет

Prev. hash:
Data: Genesis Block
Hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168 Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
Data: Send 1 BTC to Ivan
Hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1 Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
Data: Send 2 more BTC to Ivan
Hash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1

Наш прототип работает!

Заключение

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

Все эти свойства мы рассмотрим в будущих статьях.

Ссылки

Исходный код для статьи.

Показать больше

Похожие публикации

Кнопка «Наверх»