Как я проектировал блоки и транзакции в своем блокчейне на Go

от автора

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

  • Описание структуры данных и методов блока
  • Описание структуры данных и методов транзакции
  • Функции блокчейна, которые сохраняют блоки в БД и находят их там по их хешу или высоте (или еще как нибудь).

image

Это вторая статья про блокчейн для промышленности, первая тут.

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

Начнем с транзакций: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Вот ее структура данных:

type TX struct { 	DataType byte		 	TxHash string  	TxType byte	 	Timestamp int64		 	INs []TxIn 	OUTs []TxOut }  type TxIn struct { 	ThatTxHash string 	TxOutN int 	ByteCode string }  type TxOut struct { 	Value int 	ByteCode string }

В TX хранятся тип данных (для транзакции 2), хеш этой транзакции, тип самой транзакции, временная метка, а также входы и выходы. Входы TxIn хранят хеш транзакции, на выход которой ссылаются, номер этого выхода и байткод, а выходы TxOut хранят, какое-нибудь значение и тоже байткод.

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

Для создания транзакции служит функция transaction.NewTransaction(txtype byte) *TX.

Метод AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) добавляет вход к транзакции.

Метод AddTxOut(value int, data []byte) (*TxOut, error) добавляет выход к транзакции.

Метод ToBytes() []byte превращает транзакцию в байтовый срез.

Внутренняя функция preByteHash(bytes []byte) string применяется в Build() и Check() для совместимости создаваемого хеша транзакций с хешами транзакций генерируемыми из приложений на JavaScript.

Метод Build() задает хеш транзакции следующим образом: tx.TxHash = preByteHash(tx.ToBytes()).

Метод ToJSON() string преобразует транзакцию в JSON строку.

Метод FromJSON(data []byte) error загружает транзакцию из формата JSON, переданного в виде байтового слайса.

Метод Check() bool сравнивает полученных хеш из поля хеша транзакции с хешем, полученным в результате хеширования этой транзакции (без учета поля хеша).

Транзакции добавляются в блок: github.com/Rusldv/bcstartup/blob/master/block/builder.go

Структура данных блока более объемная:

type Block struct { 	DataType byte				 	BlockHeight int					         Timestamp int64				          HeaderSize int					         PrevBlockHash string				          SelfBlockHash string			 	TxsHash string			 	MerkleRoot string 	CreatorPublicKey string			 	CreatorSig string 	Version int 	TxsN int 	Txs []transaction.TX }

DataType хранит тип данных, по нему нода и отлечает блок от транзакции или других данных. Для блока это значение равно 1.

BlockHeight хранит высоту блока.
Timestamp временную метку.
HeaderSize размер блока в байтах.
PrevBlockHash хеш предыдущего блока, а SelfBlockHash — текущего.
TxsHash — это общий хеш транзакций.
MerkleRoot — корень дерева Меркла.

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

Рассмотрим его методы:
Для создания блока применяется функция block.NewBlock(): NewBlock(prevBlockHash string, height int) *Block, которая принимает хеш предыдущего блока и высоту установленную для созданного блока в блокчейне. Также задается тип блока из константы пакета types:

b.DataType = types.BLOCK_TYPE.

Метод AddTx(tx *transaction.TX) добавляет транзакцию в блок.

Метод Build() загружает значения в поля блока и генерирует и устанавливает его текущий хеш.

Метод ToBytesHeader() []byte переводит заголовок блока (без транзакций) в байтовый слайс.

Метод ToJSON() string переводит блок в формат JSON в строковом представлении данных.

Метод FromJSON(data []byte) error загружает данные из JSON в структуру блока.

Метод Check() bool генерирует хеш блока и сравнивает с заданным в поле хеша блока.

Метод GetTxsHash() string возвращает общий хеш всех транзакций в блоке.

Метод GetMerkleRoot() задает корень дерева Меркла для транзакций в блоке.

Метод Sign(privk string) подписывает блок приватным клюем создателя блока.

Метод SetHeight(height int) записывает высоту блока в поле структуры блока.

Метод GetHeight() int возвращает высоту блока так как указано в соответствующем поле структуры блока.

Метод ToGOBBytes() []byte кодирует блок в GOB формат и возвращает его в виде байтового слайса.

Метод FromGOBBytes(data []byte) error записывает данные блока в структуру блока из переданного байтового слайса в формате GOB.

Метод GetHash() string возвращает хеш данного блока.

Метод GetPrevHash() string возвращает хеш предыдущего блока.

Метод SetPublicKey(pubk string) записывает в блок публичный ключ создателя блока.

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

За сохранения в блокчейн отвечают функции пакета blockchain: github.com/Rusldv/bcstartup/tree/master/blockchain

Для этого блок должен реализовывать интерфейс IBlock:

type IGOBBytes interface { 	ToGOBBytes() []byte 	FromGOBBytes(data []byte) error }  type IBlock interface { 	IGOBBytes 	GetHash() string 	GetPrevHash() string 	GetHeight() int 	Check() bool  }

Подключение к базе данных создается один раз при инициализации пакета в функции init():

db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).

CloseDB() это обертка для db.Cloce() — вызывается после работы с функциями пакета, чтобы закрыть соединение с БД.

Функция SetTargetBlockHash(hash string) error записывает в БД хеш текущего блока с ключем заданным константой BLOCK_HASH.

Функция GetTargetBlockHash() (string, error) возвращает хеш текущего блока, хранящийся в БД.

Функция SetTargetBlockHeight(height int) error записывает в БД значение высоты блокчейна для ноды с ключем заданным константой BLOCK_HEIGHT.

Функция GetTargetBlockHeight() (int, error) возвращает высоту блокчейна для данной ноды, хранимую в БД.

Функция CheckBlock(block IBlock) bool выполняет проверку блока на корректность перед добавлением этого блока в блокчейн.

Функция AddBlock(block IBlock) error добавляет блок в блокчейн.

Функции для получения и просмотра блоков находятся в файле explore.go пакета blockchain:

Функция GetBlockByHash(hash string) (*block.Block, error) создает пустой объект блока, загружает туда блок из БД хеш которого ей передан и возвращает на него указатель.

Создание блока генезиса осуществляется функцией Genesis() error из файла genesis.go пакета blockchain.

В следующей статье речь пойдет о подключению к ноде клиентов с помощью механизма WebSocket.

ссылка на оригинал статьи https://habr.com/ru/post/499930/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *