Как я проектировал блоки и транзакции в своем блокчейне на 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.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 0

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

    Самое читаемое