Для того, щоб зрештою вийшов блокчейн, а не просто база даних, нам потрібно додати у свій проект 3 важливі елементи:
- Опис структури даних та методів блоку
- Опис структури даних та методів транзакції
- Функції блокчейна, які зберігають блоки в БД і знаходять їх там по їхньому хешу або висоті (або ще якось).
Це друга стаття про блокчейн для промисловості, перша
Згадуючи питання, які мені ставили читачі до попередньої статті цього циклу, слід зазначити: для зберігання даних блокчейну в даному випадку використовується база даних LevelDB, але ні чого не заважає використовувати будь-яку іншу, скажімо ту ж MySQL. А тепер розберемося із структурою цих даних.
Почнемо з транзакцій:
Ось її структура даних:
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 порівнює отриманий хеш з поля хешу транзакції з хешом, отриманим в результаті хешування цієї транзакції (без урахування поля хешу).
Транзакції додаються до блоку:
Структура даних блоку об'ємніша:
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:
Для цього блок має реалізовувати інтерфейс 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