Для того, чтобы в конечном счете получиляся блокчейн, а не просто база данных, нам нужно добавить в свой проект 3 важных элемента:
Это вторая статья про блокчейн для промышленности, первая тут.
Вспоминая вопросы, которые мне задавали читатели к предыдущей статье этого цикла, следует отметить: для хранения данных блокчейна в данном случае используется база данных LevelDB, но ни чего не мешает использовать любую другую, скажем ту же MySQL. А теперь разберемся со структурой этих данных.
Начнем с транзакций: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go
Вот ее структура данных:
В 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
Структура данных блока более объемная:
DataType хранит тип данных, по нему нода и отлечает блок от транзакции или других данных. Для блока это значение равно 1.
BlockHeight хранит высоту блока.
Timestamp временную метку.
HeaderSize размер блока в байтах.
PrevBlockHash хеш предыдущего блока, а SelfBlockHash — текущего.
TxsHash — это общий хеш транзакций.
MerkleRoot — корень дерева Меркла.
Далее в полях находится публичный ключ создателя блока, подпись создателя, версия блока, количество транзакций в блоке и собственно сами эти транзакции.
Рассмотрим его методы:
Для создания блока применяется функция block.NewBlock(): NewBlock(prevBlockHash string, height int) *Block, которая принимает хеш предыдущего блока и высоту установленную для созданного блока в блокчейне. Также задается тип блока из константы пакета types:
Метод 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:
Подключение к базе данных создается один раз при инициализации пакета в функции init():
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.
- Описание структуры данных и методов блока
- Описание структуры данных и методов транзакции
- Функции блокчейна, которые сохраняют блоки в БД и находят их там по их хешу или высоте (или еще как нибудь).
Это вторая статья про блокчейн для промышленности, первая тут.
Вспоминая вопросы, которые мне задавали читатели к предыдущей статье этого цикла, следует отметить: для хранения данных блокчейна в данном случае используется база данных 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.