[Перевод] Напишите свой собственный блокчейн в GO менее чем за 200 строк

Программа перевода самородков сервер Go блокчейн
[Перевод] Напишите свой собственный блокчейн в GO менее чем за 200 строк

Если вы читаете эту статью не в первый раз, пожалуйста, прочтите часть 2 -здесь!

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

Примеры данных в этом руководстве будут основаны на вашем сердцебиении в состоянии покоя. В конце концов, мы медицинская компания :-) Ради интереса запишите свою одну минутучастота пульса(ударов в минуту) и запомните это значение.

Почти каждый разработчик в мире слышал о блокчейне, но большинство до сих пор не знает, как он работает. Они могут знать о Биткойне только из-за него или, может быть, потому, что слышали о смарт-контрактах или чем-то подобном. Этот пост пытается демистифицировать блокчейны, помогая вам написать свой собственный простой блокчейн на Go, используя менее 200 строк кода! К концу этого руководства вы сможете писать и запускать собственный блокчейн локально, а также просматривать его в веб-браузере.

Есть ли лучший способ узнать о блокчейне, чем создать свой собственный?

что ты сможешь сделать

  • Создайте свой собственный блокчейн
  • Узнайте, как хеширование поддерживает целостность блокчейна
  • Узнайте, как добавлять новые блоки
  • Посмотрите, как разрешаются конфликты, когда несколько узлов генерируют блоки
  • Просмотр блокчейна в веб-браузере
  • написать новый блок
  • Получите основы блокчейна, чтобы вы могли решить, куда пойдет ваше путешествие!

что ты не можешь сделать

Чтобы упростить эту статью, мы не будем рассматривать более сложные концепции консенсуса, такие как Proof of Work и Proof of Stake. Для того, чтобы вы могли просматривать свой блокчейн и добавление блоков, мы смоделируем сетевые взаимодействия, но глубина веб-трансляции как статьи будет сохранена.

Давайте начнем!

Готов к работе

Теперь, когда мы решили программировать на Go, мы предполагаем, что у вас уже есть некоторый опыт работы с Go, вУстановитьА после настройки Go нам также необходимо получить следующие пакеты:

go get github.com/davecgh/go-spew/spew

SpewПозволяет нам просматривать чисто отформатированные в консолиstructsиslices, ты заслуживаешь это.

go get github.com/gorilla/mux

Gorilla/muxЭто обычный пакет для написания обработки веб-программ. Мы будем использовать его.

go get github.com/joho/godotenv

Gotdotenvпозволяет нам читать из корневого каталога.envфайл, поэтому вам не нужно жестко кодировать такие вещи, как HTTP-порты. Нам это тоже нужно.

Создаем в корневом каталоге.envфайл, определяющий порт для обслуживания http-запросов. Просто добавьте строку в файл:

ADDR=8080

Создайтеmain.goдокумент. Отныне все будет записано в этот файл и закодировано менее чем в 200 кодах.

Импортировать

Это то, что нам нужно импортировать и объявление пакета, мы пишем их вmain.go

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/gorilla/mux"
	"github.com/joho/godotenv"
)

модель данных

Мы определим каждый блок, который составляет блокчейнstruct. Не волнуйтесь, через минуту мы объясним, что означают все эти поля.

type Block struct {
	Index     int
	Timestamp string
	BPM       int
	Hash      string
	PrevHash  string
}

каждыйBlockОба содержат данные, которые будут записаны в блокчейн, и представляют каждую ситуацию, когда вы получаете свой пульс (помните, когда вы делали это в начале статьи?).

  • Indexгде данные записываются в блокчейн
  • Timestampзаключается в том, чтобы автоматически определять, когда записывать данные
  • BPMударов в минуту, что является вашей частотой пульса
  • Hashэто идентификатор SHA256, представляющий эту запись данных
  • PrevHashэто идентификатор SHA256 предыдущей записи в цепочке

Мы также моделируем сам блокчейн, это простоBlockсерединаslice:

var Blockchain []Block

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

Хэширование и генерация новых блоков

Так зачем нам хеширование? Есть две основные причины, по которым мы хэшируем данные:

  • Чтобы сэкономить место. Хэш получается из всех данных в блоке. В нашем примере у нас есть только несколько точек данных, но, допустим, у нас есть данные из сотен, тысяч или миллионов предыдущих фрагментов. хеш-данные в одну строку SHA256 илихешировать эти хеш-таблицынамного эффективнее, чем многократное копирование всех данных в предыдущем блоке.
  • Защитите целостность блокчейна. Сохраняя предыдущий хэш, как мы это сделали на диаграмме выше, мы можем гарантировать, что блоки в блокчейне расположены в правильном порядке. Если злоумышленник попытается манипулировать данными (например, изменить частоту сердечных сокращений, чтобы определить цену страхования жизни), хэш быстро изменится, цепочка «разорвется», и все узнают о злонамеренной цепочке и перестанут доверять ей.

Напишем функцию, которая принимаетBlockdata и создайте хэш SHA256 i .


func calculateHash(block Block) string {
	record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash
	h := sha256.New()
	h.Write([]byte(record))
	hashed := h.Sum(nil)
	return hex.EncodeToString(hashed)
}

этоcalculateHashфункция будетBlockизIndex,Timestamp,BPM, мы предоставляем блокиPrevHashЦепочка в качестве параметра и возвращает хэш SHA256 в виде строки. Теперь мы можем использовать новыйgenerateBlockдля создания нового блока, содержащего все необходимые нам элементы. Нам нужно предоставить блок перед ним, чтобы мы могли получить его хэш и частоту пульса в BPM. Не беспокойтесь о входящихBPM intпараметр. Мы обсудим это позже.

func generateBlock(oldBlock Block, BPM int) (Block, error) {

	var newBlock Block

	t := time.Now()

	newBlock.Index = oldBlock.Index + 1
	newBlock.Timestamp = t.String()
	newBlock.BPM = BPM
	newBlock.PrevHash = oldBlock.Hash
	newBlock.Hash = calculateHash(newBlock)

	return newBlock, nil
}

Обратите внимание на текущее использование времениtime.Now()автоматически записывается в блок. Также обратите внимание, что наш предыдущийcalculateHashФункция вызывается. Скопируйте из хеша предыдущего блока вPrevHash.IndexУвеличение от индекса предыдущего блока.

проверка блока

Нам нужно написать некоторые функции, чтобы гарантировать, что эти блоки не были подделаны. Так же проходим техосмотрIndexчтобы убедиться, что они увеличиваются, как ожидалось. Мы также проверим, чтобы убедиться, что нашиPrevHashс предыдущим блокомHashтакой же. Наконец, мы хотим снова запустить текущий блок с помощьюcalculateHashфункция для перепроверки хэша текущего блока. Давайте напишемisBlockValidфункция, которая делает все это и возвращаетbool. Если он проходит все наши проверки, он возвращаетtrue:

func isBlockValid(newBlock, oldBlock Block) bool {
	if oldBlock.Index+1 != newBlock.Index {
		return false
	}

	if oldBlock.Hash != newBlock.PrevHash {
		return false
	}

	if calculateHash(newBlock) != newBlock.Hash {
		return false
	}

	return true
}

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

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

Для этого мы просто сравниваем длины цепочек:

func replaceChain(newBlocks []Block) {
	if len(newBlocks) > len(Blockchain) {
		Blockchain = newBlocks
	}
}

Если вы зашли так далеко, поощряйте себя! По сути, мы написали внутренности блокчейна с различными необходимыми нам функциями.

Теперь нам нужен удобный способ просмотра нашего блокчейна и записи в него, в идеале мы можем показать его нашим друзьям в веб-браузере!

веб сервер

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

Мы будем использовать ваш ранее загруженныйGorilla/muxпакет, чтобы сделать тяжелую работу для нас.

мы позвоним позжеrunФункция для создания сервера.

func run() error {
	mux := makeMuxRouter()
	httpAddr := os.Getenv("ADDR")
	log.Println("Listening on ", os.Getenv("ADDR"))
	s := &http.Server{
		Addr:           ":" + httpAddr,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if err := s.ListenAndServe(); err != nil {
		return err
	}

	return nil
}

Обратите внимание, что выбранный нами порт относится к ранее созданному порту..envдокумент. Мы используемlog.PrintlnОтправьте себе живое консольное сообщение, чтобы запустить наш сервер. Мы немного настроили оружие, а затемListenAndServeнастроить. Довольно стандартный Go.

Теперь нам нужно написатьmakeMuxRouterфункция, которая будет определять все обработчики. Для просмотра и записи в нашу цепочку блоков в браузере нам нужны только два маршрута, и мы сделаем их простыми. Если мы отправимGETзапроситьlocalhost, мы рассмотрим блокчейн. Если мы отправимPOSTзапрос, мы можем написать.

func makeMuxRouter() http.Handler {
	muxRouter := mux.NewRouter()
	muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
	muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
	return muxRouter
}

это нашеGETпроцессор.

func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
	bytes, err := json.MarshalIndent(Blockchain, "", "  ")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, string(bytes))
}

Нам просто нужно записать полную цепочку блоков в формате JSON, доступ к которой можно получить с помощьюlocalhost:8080Просмотр в любом браузере. мы в.envдокумент будетADDRДля переменной установлено значение 8080, если вы измените ее, убедитесь, что вы указали правильный порт.

нашPOSTЗапрос немного сложный (их не много). Во-первых, нам нужен новыйMessage struct. Позже мы объясним, зачем нам это нужно.

type Message struct {
	BPM int
}

Ниже приведен код для написания обработчика нового блока. Мы приглашаем вас посмотреть его снова после того, как вы его прочтете.

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
	var m Message

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&m); err != nil {
		respondWithJSON(w, r, http.StatusBadRequest, r.Body)
		return
	}
	defer r.Body.Close()

	newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
	if err != nil {
		respondWithJSON(w, r, http.StatusInternalServerError, m)
		return
	}
	if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
		newBlockchain := append(Blockchain, newBlock)
		replaceChain(newBlockchain)
		spew.Dump(Blockchain)
	}

	respondWithJSON(w, r, http.StatusCreated, newBlock)

}

использовать автономныйMessageПричина структуры — получение тела запроса JSON POST, которое мы будем использовать для записи новых блоков. Это позволяет нам просто отправить запрос POST со следующим телом, и наш обработчик заполнит остальную часть блока за нас:

{"BPM":50}

50является примером частоты пульса в единицах минут. Измените частоту пульса на целочисленное значение.

Расшифровка тела запроса вvar m Messageструктуру, передав предыдущий блок и передав новую частоту пульса ранее записанномуgenerateBlockФункция для создания нового блока. Это все, что нужно функции для создания нового блока. Используем ранее созданныйisBlockValidфункция, быстрая проверка, чтобы убедиться, что новый блок в порядке.

некоторые примечания

  • _spew.Dump_ — это удобная функция, которая выводит нашу структуру на консоль. Это полезно для отладки.
  • Для тестирования запросов POST нам нравится использовать Postman.curlОтлично работает, если вы не можете выйти из терминала.

Когда наш POST-запрос завершается успешно или неудачно, мы хотим получать соответствующие уведомления. Мы использовали небольшую функцию-оболочкуrespondWithJSONДавай, дай нам знать, что случилось. Помните, в Go никогда не игнорируйте их.обращаться с ними изящно.

func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
	response, err := json.MarshalIndent(payload, "", "  ")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("HTTP 500: Internal Server Error"))
		return
	}
	w.WriteHeader(code)
	w.Write(response)
}

Почти готово!

Давайте свяжем все различные функции блокчейна, веб-обработчики и веб-серверы в коротком, ясномmainВ функции:

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		t := time.Now()
		genesisBlock := Block{0, t.String(), 0, "", ""}
		spew.Dump(genesisBlock)
		Blockchain = append(Blockchain, genesisBlock)
	}()
	log.Fatal(run())

}

Как это происходит?

  • godotenv.Load()позволяет нам начать с корневого каталога.envПеременные, такие как номера портов, считываются в файле, поэтому нам не нужно жестко кодировать их во всем приложении (отвратительно!).
  • genesisBlockдаmainСамая важная часть функции. Нам нужно предоставить нашей цепочке блоков начальный блок, иначе новый блок не сможет сравнить свой предыдущий хэш ни с чем, потому что предыдущий хеш не существует.
  • Мы изолировали начальный блок в его собственную процедуру Go, чтобы мы могли отделить проблемы от нашей логики блокчейна и логики веб-сервера. Но он просто работает более элегантно без подпрограмм Go.

Очень хороший! Мы сделали!

Вот полный код:

А теперь поговорим о чем-нибудь интересном. Давай попробуем :-)

использоватьgo run main.goЗапустите приложение из терминала

В терминале мы видим, что веб-сервер запущен и работает, и получаем распечатку нашего начального блока.

Теперь используйте свой номер порта для доступаlocalhost, 8080 для нас. Как и ожидалось, мы видим тот же начальный блок.

Теперь давайте отправим несколько запросов POST для добавления фрагментов. Используя Postman, мы добавим несколько новых блоков с разными BPM.

Обновим браузер. Вуаля, теперь мы видим все новые блоки в цепочке с новым блокомPrevHashсо старыми блокамиHashсовпало, как мы и ожидали!

Следующий шаг

Поздравляем! Вы просто пишете свой собственный блокчейн с правильным хешированием и проверкой блоков. Теперь вы сможете взять под контроль свое путешествие по блокчейну и изучить более сложные темы, такие как Proof of Work, Proof of Stake, смарт-контракты, Dapps, сайдчейны и другие.

Что не обсуждается в этом руководстве, так это то, как использовать Proof of Work для добычи новых блоков. Это будет чистое руководство, но существует большое количество блокчейнов без механизма проверки работоспособности. Кроме того, трансляции в настоящее время имитируются путем записи и просмотра блокчейна на веб-сервере. В этом руководстве нет компонентов P2P.

Если вы хотите, чтобы мы добавили такие вещи, как подтверждение работы и отношений, обязательно опубликуйте в нашемTelegramрасскажите нам и следите за нашимиTwitter! Это лучший способ связи с нами. Задавайте нам вопросы, оставляйте отзывы и предлагайте новые уроки. Мы хотели бы услышать от вас.

По многочисленным просьбам мы добавили продолжение этого урока! Проверь их!

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


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.