Pentru ca, în cele din urmă, să avem un blockchain și nu doar o bază de date, trebuie să adăugăm în proiectul nostru 3 elemente importante:
- Descrierea structurii datelor și metodelor blocului
- Descrierea structurii datelor și metodelor tranzacției
- Funcțiile blockchain-ului care salvează blocurile în baza de date și le găsesc acolo după hash-ul sau înălțimea lor (sau altfel).

Aceasta este a doua articol despre blockchain pentru industrie, prima .
Amintind întrebările pe care mi le-au adresat cititorii articolului precedent din acest ciclu, trebuie să menționez: pentru stocarea datelor blockchain-ului, în acest caz se folosește baza de date LevelDB, dar nimic nu ne împiedică să utilizăm orice altă, să zicem aceeași MySQL. Acum să analizăm structura acestor date.
Să începem cu tranzacțiile:
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
}În TX sunt stocate tipul de date (pentru tranzacție 2), hash-ul acestei tranzacții, tipul tranzacției, marca de timp, precum și intrările și ieșirile. Intrările TxIn stochează hash-ul tranzacției la care fac referire, numărul acestei ieșiri și codul în bytes, iar ieșirile TxOut stochează o valoare și, de asemenea, codul în bytes.
Acum să vedem ce acțiuni poate realiza tranzacția asupra datelor sale, adică să analizăm metodele.
Funcția care servește pentru crearea unei tranzacții este transaction.NewTransaction(txtype byte) *TX.
Metoda AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) adaugă o intrare la tranzacție.
Metoda AddTxOut(value int, data []byte) (*TxOut, error) adaugă o ieșire la tranzacție.
Metoda ToBytes() []byte transformă tranzacția într-un slice de bytes.
Funcția internă preByteHash(bytes []byte) string este utilizată în Build() și Check() pentru a asigura compatibilitatea hash-ului tranzacțiilor create cu hash-urile tranzacțiilor generate de aplicații JavaScript.
Metoda Build() setează hash-ul tranzacției astfel: tx.TxHash = preByteHash(tx.ToBytes()).
Metoda ToJSON() string transformă tranzacția într-un șir JSON.
Metoda FromJSON(data []byte) error încarcă tranzacția din formatul JSON, transmis ca un slice de bytes.
Metoda Check() bool compară hash-ul obținut din câmpul hash-ului tranzacției cu hash-ul obținut în urma hash-ării acestei tranzacții (fără a lua în considerare câmpul hash).
Tranzacțiile sunt adăugate în bloc:
Structura de date a blocului este mai cuprinzătoare:
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, iar nodul le distinge între blocuri și tranzacții sau alte date. Pentru un bloc, această valoare este 1.
BlockHeight stochează înălțimea blocului.
Timestamp este un marcaj temporar.
HeaderSize este dimensiunea blocului în bytes.
PrevBlockHash este hash-ul blocului anterior, iar SelfBlockHash este hash-ul curent.
TxsHash este hash-ul total al tranzacțiilor.
MerkleRoot este rădăcina copacului Merkle.
Următoarele câmpuri conțin cheia publică a creatorului blocului, semnătura creatorului, versiunea blocului, numărul de tranzacții în bloc și, de fapt, aceste tranzacții.
Să examinăm metodele sale:
Pentru a crea un bloc, se folosește funcția block.NewBlock(): NewBlock(prevBlockHash string, height int) *Block, care primește hash-ul blocului anterior și înălțimea stabilită pentru blocul creat în blockchain. De asemenea, se setează tipul blocului din constanta pachetului types:
b.DataType = types.BLOCK_TYPE.Metoda AddTx(tx *transaction.TX) adaugă o tranzacție în bloc.
Metoda Build() încarcă valorile în câmpurile blocului și generează și stabilește hash-ul său curent.
Metoda ToBytesHeader() []byte convertește antetul blocului (fără tranzacții) într-un slice de bytes.
Metoda ToJSON() string convertește blocul în format JSON sub formă de șir de date.
Metoda FromJSON(data []byte) error încarcă datele din JSON în structura blocului.
Metoda Check() bool generează hash-ul blocului și îl compară cu cel specificat în câmpul hash-ului blocului.
Metoda GetTxsHash() string returnează hash-ul total al tuturor tranzacțiilor din bloc.
Metoda GetMerkleRoot() stabilește rădăcina copacului Merkle pentru tranzacțiile din bloc.
Metoda Sign(privk string) semnează blocul 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 codează blocul în format GOB și îl returnează sub formă de slice de bytes.
Metoda FromGOBBytes(data []byte) error scrie datele blocului în structura blocului din slice-ul de bytes transmis în format GOB.
Metoda GetHash() string returnează hash-ul acestui bloc.
Metoda GetPrevHash() string returnează hash-ul blocului anterior.
Metoda SetPublicKey(pubk string) scrie în bloc cheia publică a creatorului blocului.
Astfel, folosind metodele obiectului Block, putem converti cu ușurință într-un format pentru transfer pe rețea și salvare în baza de date LevelDB.
Funcțiile pachetului blockchain sunt responsabile pentru salvarea în blockchain:
Pentru aceasta, 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
}Conectarea la baza de date se creează o singură dată la inițializarea pachetului în funcția init():
db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).CloseDB() este o interfață pentru db.Close() — este apelată după utilizarea funcțiilor pachetului pentru a închide conexiunea la baza de date.
Funcția SetTargetBlockHash(hash string) error scrie în baza de date hash-ul blocului curent cu cheia furnizată de constanta BLOCK_HASH.
Funcția GetTargetBlockHash() (string, error) returnează hash-ul blocului curent stocat în baza de date.
Funcția SetTargetBlockHeight(height int) error scrie în baza de date valoarea înălțimii blockchain-ului pentru nodul cu cheia furnizată de constanta BLOCK_HEIGHT.
Funcția GetTargetBlockHeight() (int, error) returnează înălțimea blockchain-ului pentru acest nod, stocată în baza de date.
Funcția CheckBlock(block IBlock) bool efectuează verificarea blocului pentru validitate înainte de a-l adăuga în blockchain.
Funcția AddBlock(block IBlock) error adaugă blocul în blockchain.
Funcțiile pentru a obține și vizualiza blocurile se află în fișierul explore.go al pachetului blockchain:
Funcția GetBlockByHash(hash string) (*block.Block, error) creează un obiect de bloc gol, încarcă un bloc din baza de date ai cărui hash este furnizat și returnează un pointer către el.
Crearea blocului genesis se realizează prin funcția Genesis() error din fișierul genesis.go al pachetului blockchain.
În următorul articol, vom discuta despre conectarea la nodul clienților prin intermediul mecanismului WebSocket.
Sursa: habr.com
