Как проектирах блокове и транзакции в моя блокчейн на Go

За да се получи блокчейн, а не просто база данни, е необходимо да добавим в проекта си 3 важни елемента:

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

Как проектирах блокове и транзакции в моя блокчейн на Go

Това е втората статия за блокчейна в индустрията, първата тук.

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

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

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.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