За да се получи блокчейн, а не просто база данни, е необходимо да добавим в проекта си 3 важни елемента:
- Описание на структурата на данните и методите на блока
- Описание на структурата на данните и методите на транзакцията
- Функции на блокчейна, които запазват блоковете в базата данни и ги намират там по техния хеш или височина (или по друг начин).

Това е втората статия за блокчейна в индустрията, първата .
Спомняйки си въпросите, които читателите ми задаваха относно предишната статия от този цикъл, трябва да се отбележи: за съхранение на данните от блокчейна в този случай се използва базата данни LevelDB, но нищо не пречи да се използва любa друга, например 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 сравнява полученото хеш от полето за хеш на транзакцията с хеша, получен в резултат на хеширането на тази транзакция (без да се взема предвид полето за хеш).
Транзакциите се добавят в блока:
Структурата на данните на блока е по-обширна:
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.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
