Как проектирах блокове и транзакции в моя блокчейн на 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

Структурата на данните на блока е по-обширна:

тип 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: 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.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

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster