Як я праектаваў блокі і транзакцыі ў сваім блокчейне на Go

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

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

Як я праектаваў блокі і транзакцыі ў сваім блокчейне на Go

Гэта другі артыкул пра блокчэйн для прамысловасці, першы тут.

Успамінаючы пытанні, якія мне задавалі чытачы да папярэдняга артыкула гэтага цыклу, варта адзначыць: для захоўвання дадзеных блокчейна ў дадзеным выпадку выкарыстоўваецца база дадзеных 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.

Крыніца: habr.com

Дадаць каментар