За да получим в крайна сметка блокчейн, а не просто база данни, трябва да добавим в проекта си 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.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 сравнява получения хеш от полето на хеша на транзакцията с хеш, получен в резултат на хеширане на тази транзакция (без да се отчита полето на хеша).
Транзакциите се добавят в блока:
Структурата на данните на блока е по-обширна:
тип Block структури {
DataType байт
BlockHeight int
Timestamp int64
HeaderSize int
PrevBlockHash низ
SelfBlockHash низ
TxsHash низ
MerkleRoot низ
CreatorPublicKey низ
CreatorSig низ
Version int
TxsN int
Txs []transaction.TX
}DataType съхранява типа данни, по който възелът разграничават блока от транзакциите или други данни. За блока това значение е равно на 1.
BlockHeight съхранява височината на блока.
Timestamp е времевата маркировка.
HeaderSize е размерът на блока в байтове.
PrevBlockHash е хешът на предишния блок, а SelfBlockHash — на текущия.
TxsHash е общият хеш на транзакциите.
MerkleRoot е коренът на Мерклото дърво.
По-долу в полетата се намира публичният ключ на създателя на блока, подписът на създателя, версията на блока, броят на транзакциите в блока и самите транзакции.
Нека разгледаме неговите методи:
За създаване на блок се използва функцията block.NewBlock(): NewBlock(prevBlockHash низ, height int) *Block, която приема хеш на предишния блок и височина, зададена за създадения блок в блокчейна. Също така се задава типът блок от константата на пакета types:
b.DataType = types.BLOCK_TYPE.Методът AddTx(tx *transaction.TX) добавя транзакция в блока.
Методът Build() зарежда стойностите в полетата на блока и генерира и задава текущия хеш.
Методът ToBytesHeader() []byte преобразува заглавието на блока (без транзакции) в байтов масив.
Методът ToJSON() низ преобразува блока в JSON формат в текстово представяне на данните.
Методът FromJSON(data []byte) грешка зарежда данните от JSON в структурата на блока.
Методът Check() бул генерира хеш на блока и го сравнява с посочения в полето за хеш блока.
Методът GetTxsHash() низ връща общия хеш на всички транзакции в блока.
Методът GetMerkleRoot() задава корена на Мерклото дърво за транзакциите в блока.
Методът Sign(privk низ) подписва блока с частния ключ на създателя на блока.
Методът SetHeight(height int) записва височината на блока в полето на структурата на блока.
Методът GetHeight() int връща височината на блока така, както е посочено в съответното поле на структурата на блока.
Методът ToGOBBytes() []byte кодира блока в GOB формат и го връща под формата на байтов масив.
Методът FromGOBBytes(data []byte) грешка записва данните на блока в структурата на блока от предадения байтов масив в GOB формат.
Методът GetHash() низ връща хеша на настоящия блок.
Методът GetPrevHash() низ връща хеша на предишния блок.
Методът SetPublicKey(pubk низ) записва в блока публичния ключ на създателя на блока.
Следователно, с помощта на методите на обекта 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.Close() — извиква се след работа с функциите на пакета, за да затвори връзката с БД.
Функцията 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
