Cum am conceput blocuri și tranzacții în blockchain-ul meu Go

Pentru a ajunge în cele din urmă cu un blockchain și nu doar cu o bază de date, trebuie să adăugăm 3 elemente importante proiectului nostru:

  • Descrierea structurii și metodelor de date bloc
  • Descrierea structurii datelor și a metodelor de tranzacție
  • Funcții blockchain care salvează blocurile într-o bază de date și le găsesc acolo după hash sau înălțime (sau altceva).

Cum am conceput blocuri și tranzacții în blockchain-ul meu Go

Acesta este al doilea articol despre blockchain pentru industrie, primul aici.

Amintindu-ne de întrebările pe care mi le-au pus cititorii cu privire la articolul precedent din această serie, trebuie menționat: în acest caz, baza de date LevelDB este folosită pentru a stoca date blockchain, dar nimic nu vă împiedică să utilizați orice alt, să zicem, MySQL. Acum să ne uităm la structura acestor date.

Să începem cu tranzacțiile: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Iată structura sa de date:

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 stochează tipul de date (pentru tranzacția 2), hash-ul acelei tranzacții, tipul tranzacției în sine, un marcaj de timp și intrările și ieșirile. Intrările TxIn stochează hash-ul tranzacției a cărei ieșire este referită, numărul acestei ieșiri și bytecode, iar ieșirile TxOut stochează o anumită valoare și, de asemenea, bytecode.

Acum să vedem ce acțiuni poate efectua o tranzacție asupra datelor sale, de exemplu. Să ne uităm la metode.

Pentru a crea o tranzacție, utilizați funcția transaction.NewTransaction(txtype byte) *TX.

Metoda AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, eroare) adaugă o intrare la tranzacție.

Metoda AddTxOut(value int, data []byte) (*TxOut, eroare) adaugă o ieșire tranzacției.

Metoda ToBytes() []byte transformă tranzacția într-o porțiune de octeți.

Funcția internă preByteHash(bytes []byte) șir este folosită în Build() și Check() pentru a face hash-ul tranzacției generat compatibil cu hash-urile tranzacției generate din aplicațiile JavaScript.

Metoda Build() setează hash-ul tranzacției după cum urmează: tx.TxHash = preByteHash(tx.ToBytes()).

Metoda șirului ToJSON() convertește o tranzacție într-un șir JSON.

Metoda de eroare FromJSON(data []byte) încarcă o tranzacție din formatul JSON transmis ca o porțiune de octeți.

Metoda bool Check() compară hashul rezultat din câmpul hash al tranzacției cu hash-ul obținut ca urmare a hashării acestei tranzacții (ignorând câmpul hash).

Tranzacțiile sunt adăugate la bloc: github.com/Rusldv/bcstartup/blob/master/block/builder.go

Structura de date bloc este mai voluminoasă:

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 stochează tipul de date, nodul îl folosește și distinge blocul de o tranzacție sau alte date. Pentru un bloc, această valoare este 1.

BlockHeight stochează înălțimea blocului.
Timp de timp și de timp.
HeaderSize este dimensiunea blocului în octeți.
PrevBlockHash este hash-ul blocului anterior, iar SelfBlockHash este hash-ul celui curent.
TxsHash este un hash general al tranzacțiilor.
MerkleRoot este rădăcina arborelui Merkle.

Mai departe în câmpuri se află cheia publică a creatorului blocului, semnătura creatorului, versiunea blocului, numărul de tranzacții din bloc și aceste tranzacții în sine.

Să ne uităm la metodele sale:
Pentru a crea un bloc, utilizați funcția block.NewBlock(): NewBlock(prevBlockHash șir, înălțime int) *Block, care preia hash-ul blocului anterior și înălțimea setata pentru blocul creat în blockchain. Tipul de bloc este setat și din constanta pachetului de tipuri:

b.DataType = types.BLOCK_TYPE.

Metoda AddTx(tx *transaction.TX) adaugă o tranzacție la un bloc.

Metoda Build() încarcă valori în câmpurile blocului și generează și setează hash-ul curent.

Metoda ToBytesHeader() []byte convertește antetul blocului (fără tranzacții) într-o porțiune de octeți.

Metoda șirului ToJSON() convertește blocul în format JSON într-o reprezentare în șir a datelor.

Metoda de eroare FromJSON(data []byte) încarcă datele din JSON într-o structură de bloc.

Metoda Check() bool generează un bloc hash și îl compară cu cel specificat în câmpul bloc hash.

Metoda șirului GetTxsHash() returnează hash-ul total al tuturor tranzacțiilor din bloc.

Metoda GetMerkleRoot() specifică rădăcina arborelui Merkle pentru tranzacțiile dintr-un bloc.

Metoda Sign(privk string) semnează un bloc cu cheia privată a creatorului blocului.

Metoda SetHeight(height int) scrie înălțimea blocului în câmpul structurii blocului.

Metoda GetHeight() int returnează înălțimea blocului așa cum este specificată în câmpul corespunzător al structurii blocului.

Metoda ToGOBBytes() []byte codifică un bloc în format GOB și îl returnează ca o porțiune de octeți.

Metoda de eroare FromGOBBytes(data []byte) scrie datele blocului în structura blocului din porțiunea de octeți transmisă în format GOB.

Metoda șirului GetHash() returnează hash-ul blocului dat.

Metoda șirului GetPrevHash() returnează hash-ul blocului anterior.

Metoda SetPublicKey(pubk string) scrie cheia publică a creatorului blocului în bloc.

Astfel, folosind metodele obiectului Block, îl putem converti cu ușurință într-un format pentru transmitere prin rețea și salvare în baza de date LevelDB.

Funcțiile pachetului blockchain sunt responsabile pentru salvarea în blockchain: github.com/Rusldv/bcstartup/tree/master/blockchain

Pentru a face acest lucru, blocul trebuie să implementeze interfața IBlock:

type IGOBBytes interface {
	ToGOBBytes() []byte
	FromGOBBytes(data []byte) error
}

type IBlock interface {
	IGOBBytes
	GetHash() string
	GetPrevHash() string
	GetHeight() int
	Check() bool

}

Conexiunea la baza de date este creată o dată când pachetul este inițializat în funcția init():

db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).

CloseDB() este un wrapper pentru db.Cloce() - apelat după lucrul cu funcțiile pachetului pentru a închide conexiunea la baza de date.

Funcția de eroare SetTargetBlockHash(șir hash) scrie hash-ul blocului curent cu cheia specificată de constanta BLOCK_HASH în baza de date.

Funcția GetTargetBlockHash() (șir, eroare) returnează hash-ul blocului curent stocat în baza de date.

Funcția de eroare SetTargetBlockHeight(height int) scrie în baza de date valoarea înălțimii blockchain pentru nod cu cheia specificată de constanta BLOCK_HEIGHT.

Funcția GetTargetBlockHeight() (int, error) returnează înălțimea blockchain-ului pentru un anumit nod, stocat în baza de date.

Funcția bool CheckBlock(block IBlock) verifică corectitudinea unui bloc înainte de a adăuga acest bloc la blockchain.

Funcția de eroare AddBlock(block IBlock) adaugă un bloc la blockchain.

Funcțiile pentru preluarea și vizualizarea blocurilor se află în fișierul explore.go al pachetului blockchain:

Funcția GetBlockByHash(șir hash) (*block.Block, error) creează un obiect bloc gol, încarcă un bloc în el din baza de date, al cărui hash i-a fost transmis și returnează un pointer către acesta.

Crearea unui bloc genesis este realizată de funcția de eroare Genesis() din fișierul genesis.go al pachetului blockchain.

Următorul articol va vorbi despre conectarea clienților la un nod folosind mecanismul WebSocket.

Sursa: www.habr.com

Adauga un comentariu