Як я проектував блоки та транзакції у своєму блокчейні на 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. New Transaction (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

Додати коментар або відгук