Perché alla fine si ottenga un blockchain e non semplicemente un database, dobbiamo aggiungere al nostro progetto 3 elementi importanti:
- Descrizione della struttura dei dati e dei metodi del blocco
- Descrizione della struttura dei dati e dei metodi della transazione
- Funzioni del blockchain che salvano i blocchi nel DB e li trovano lì attraverso il loro hash o altezza (o in altro modo).

Questo è il secondo articolo sul blockchain per l'industria, il primo .
Ricordando le domande che i lettori mi hanno posto riguardo all'articolo precedente di questo ciclo, va notato: per la memorizzazione dei dati del blockchain in questo caso viene utilizzato il database LevelDB, ma nulla impedisce di usare qualsiasi altro, ad esempio lo stesso MySQL. Ora andiamo a vedere la struttura di questi dati.
Iniziamo con le transazioni:
Ecco la sua struttura dati:
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
}In TX vengono memorizzati il tipo di dato (per la transazione 2), l'hash di questa transazione, il tipo della transazione stessa, il timestamp e anche gli ingressi e le uscite. Gli ingressi TxIn memorizzano l'hash della transazione a cui si riferiscono, il numero di tale uscita e il bytecode, mentre le uscite TxOut memorizzano un valore e anche il bytecode.
Ora vediamo quali azioni sulle proprie informazioni può eseguire una transazione, cioè analizziamo i metodi.
La funzione per creare una transazione è transaction.NewTransaction(txtype byte) *TX.
Il metodo AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) aggiunge un ingresso alla transazione.
Il metodo AddTxOut(value int, data []byte) (*TxOut, error) aggiunge un'uscita alla transazione.
Il metodo ToBytes() []byte trasforma la transazione in un array di byte.
La funzione interna preByteHash(bytes []byte) string viene utilizzata in Build() e Check() per la compatibilità dell'hash creato per le transazioni con gli hash delle transazioni generati da applicazioni in JavaScript.
Il metodo Build() imposta l'hash della transazione nel seguente modo: tx.TxHash = preByteHash(tx.ToBytes()).
Il metodo ToJSON() string converte la transazione in una stringa JSON.
Il metodo FromJSON(data []byte) error carica la transazione dal formato JSON, passato come slice di byte.
Il metodo Check() bool confronta l'hash ottenuto dal campo dell'hash della transazione con l'hash ottenuto dal processo di hashing di questa transazione (senza considerare il campo dell'hash).
Le transazioni vengono aggiunte al blocco:
La struttura dei dati del blocco è più ampia:
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 memorizza il tipo di dati, utilizzato dal nodo per differenziare il blocco dalle transazioni o da altri dati. Per un blocco, questo valore è pari a 1.
BlockHeight memorizza l'altezza del blocco.
Timestamp è il timestamp.
HeaderSize è la dimensione del blocco in byte.
PrevBlockHash è l'hash del blocco precedente, mentre SelfBlockHash è l'attuale.
TxsHash è l'hash totale delle transazioni.
MerkleRoot è la radice dell'albero di Merkle.
Successivamente, nei campi si trova la chiave pubblica del creatore del blocco, la firma del creatore, la versione del blocco, il numero di transazioni nel blocco e, infine, queste stesse transazioni.
Esaminiamo i suoi metodi:
Per creare un blocco si utilizza la funzione block.NewBlock(): NewBlock(prevBlockHash string, height int) *Block, che accetta l'hash del blocco precedente e l'altezza stabilita per il blocco creato nella blockchain. Viene anche impostato il tipo di blocco dalla costante del pacchetto types:
b.DataType = types.BLOCK_TYPE.Il metodo AddTx(tx *transaction.TX) aggiunge una transazione al blocco.
Il metodo Build() carica i valori nei campi del blocco e genera e imposta il suo hash attuale.
Il metodo ToBytesHeader() []byte converte l'intestazione del blocco (senza transazioni) in uno slice di byte.
Il metodo ToJSON() string converte il blocco in formato JSON in una rappresentazione stringa dei dati.
Il metodo FromJSON(data []byte) error carica i dati da JSON nella struttura del blocco.
Il metodo Check() bool genera l'hash del blocco e lo confronta con quello specificato nel campo dell'hash del blocco.
Il metodo GetTxsHash() string restituisce l'hash totale di tutte le transazioni nel blocco.
Il metodo GetMerkleRoot() imposta la radice dell'albero di Merkle per le transazioni nel blocco.
Il metodo Sign(privk string) firma il blocco con la chiave privata del creatore del blocco.
Il metodo SetHeight(height int) registra l'altezza del blocco nel campo della struttura del blocco.
Il metodo GetHeight() int restituisce l'altezza del blocco così come indicato nel corrispondente campo della struttura del blocco.
Il metodo ToGOBBytes() []byte codifica il blocco in formato GOB e lo restituisce come uno slice di byte.
Il metodo FromGOBBytes(data []byte) error registra i dati del blocco nella struttura del blocco dall'apposito slice di byte in formato GOB.
Il metodo GetHash() string restituisce l'hash di questo blocco.
Il metodo GetPrevHash() string restituisce l'hash del blocco precedente.
Il metodo SetPublicKey(pubk string) registra nel blocco la chiave pubblica del creatore del blocco.
In questo modo, utilizzando i metodi dell'oggetto Block, possiamo facilmente convertirlo in un formato per la trasmissione in rete e per il salvataggio nel database LevelDB.
Le funzioni del pacchetto blockchain si occupano della salvaguardia nella blockchain:
A tal fine, il blocco deve implementare l'interfaccia IBlock:
type IGOBBytes interface {
ToGOBBytes() []byte
FromGOBBytes(data []byte) error
}
type IBlock interface {
IGOBBytes
GetHash() string
GetPrevHash() string
GetHeight() int
Check() bool
}La connessione al database viene creata una sola volta durante l'inizializzazione del pacchetto nella funzione init():
db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).CloseDB() è un wrapper per db.Close() — viene chiamato dopo essere stati a contatto con le funzioni del pacchetto, per chiudere la connessione al database.
La funzione SetTargetBlockHash(hash string) error scrive nel database l'hash dell'attuale blocco con la chiave definita dalla costante BLOCK_HASH.
La funzione GetTargetBlockHash() (string, error) restituisce l'hash dell'attuale blocco, memorizzato nel database.
La funzione SetTargetBlockHeight(height int) error scrive nel database il valore dell'altezza della blockchain per il nodo con la chiave definita dalla costante BLOCK_HEIGHT.
La funzione GetTargetBlockHeight() (int, error) restituisce l'altezza della blockchain per questo nodo, memorizzata nel database.
La funzione CheckBlock(block IBlock) bool esegue il controllo del blocco per verificarne la correttezza prima di aggiungerlo alla blockchain.
La funzione AddBlock(block IBlock) error aggiunge un blocco alla blockchain.
Le funzioni per ottenere e visualizzare i blocchi si trovano nel file explore.go del pacchetto blockchain:
La funzione GetBlockByHash(hash string) (*block.Block, error) crea un oggetto blocco vuoto, carica il blocco dal database il cui hash è passato e restituisce un puntatore ad esso.
La creazione del blocco genesi avviene tramite la funzione Genesis() error nel file genesis.go del pacchetto blockchain.
Nel prossimo articolo si parlerà della connessione ai nodi dei client attraverso il meccanismo WebSocket.
Fonte: habr.com
