Per arrivare alla fine ad avere una blockchain e non solo un database, dobbiamo aggiungere 3 elementi importanti al nostro progetto:
- Descrizione della struttura e dei metodi dei dati del blocco
- Descrizione della struttura dei dati e delle modalitΓ di transazione
- Funzioni blockchain che salvano i blocchi in un database e li trovano lì in base al loro hash o altezza (o qualcos'altro).
Questo Γ¨ il secondo articolo sulla blockchain per lβindustria, il primo
Ricordando le domande che i lettori mi hanno posto sull'articolo precedente di questa serie, va notato: in questo caso, il database LevelDB viene utilizzato per archiviare i dati blockchain, ma nulla ti impedisce di utilizzare qualsiasi altro, ad esempio, MySQL. Ora diamo un'occhiata alla struttura di questi dati.
Cominciamo 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
}
TX memorizza il tipo di dati (per la transazione 2), l'hash di quella transazione, il tipo della transazione stessa, un timestamp e input e output. Gli input TxIn memorizzano l'hash della transazione a cui si fa riferimento all'output, il numero di questo output e il bytecode e gli output TxOut memorizzano un valore e anche un bytecode.
Vediamo ora quali azioni puΓ² eseguire una transazione sui suoi dati, ovvero Diamo un'occhiata ai metodi.
Per creare una transazione, utilizzare la funzione transazione.NewTransaction(txtype byte) *TX.
Il metodo AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, errore) aggiunge un input alla transazione.
Il metodo AddTxOut(value int, data []byte) (*TxOut, error) aggiunge un output alla transazione.
Il metodo ToBytes() []byte trasforma la transazione in una sezione di byte.
La stringa della funzione interna preByteHash(bytes []byte) viene utilizzata in Build() e Check() per rendere compatibile l'hash della transazione generato con gli hash della transazione generati dalle applicazioni JavaScript.
Il metodo Build() imposta l'hash della transazione come segue: tx.TxHash = preByteHash(tx.ToBytes()).
Il metodo stringa ToJSON() converte una transazione in una stringa JSON.
Il metodo di errore FromJSON(data []byte) carica una transazione dal formato JSON passato come una sezione di byte.
Il metodo Check() bool confronta l'hash risultante dal campo hash della transazione con l'hash ottenuto come risultato dell'hashing di questa transazione (ignorando il campo hash).
Le transazioni vengono aggiunte al blocco:
La struttura dei dati a blocchi Γ¨ piΓΉ voluminosa:
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, il nodo lo utilizza e distingue il blocco da una transazione o altri dati. Per un blocco questo valore Γ¨ 1.
BlockHeight memorizza l'altezza del blocco.
Marca temporale.
HeaderSize Γ¨ la dimensione del blocco in byte.
PrevBlockHash Γ¨ l'hash del blocco precedente e SelfBlockHash Γ¨ l'hash di quello corrente.
TxsHash Γ¨ un hash generale delle transazioni.
MerkleRoot Γ¨ la radice dell'albero Merkle.
Inoltre nei campi c'Γ¨ la chiave pubblica del creatore del blocco, la firma del creatore, la versione del blocco, il numero di transazioni nel blocco e queste transazioni stesse.
Diamo un'occhiata ai suoi metodi:
Per creare un blocco, utilizzare la funzione block.NewBlock(): NewBlock(prevBlockHash string, Height int) *Block, che prende l'hash del blocco precedente e l'altezza impostata per il blocco creato nella blockchain. Il tipo di blocco viene impostato anche dalla costante del pacchetto Types:
b.DataType = types.BLOCK_TYPE.
Il metodo AddTx(tx *transaction.TX) aggiunge una transazione a un blocco.
Il metodo Build() carica i valori nei campi del blocco e genera e imposta il suo hash corrente.
Il metodo ToBytesHeader() []byte converte l'intestazione del blocco (senza transazioni) in una sezione di byte.
Il metodo stringa ToJSON() converte il blocco in formato JSON in una rappresentazione di stringa dei dati.
Il metodo di errore FromJSON(data []byte) carica i dati da JSON in una struttura a blocchi.
Il metodo Check() bool genera un hash del blocco e lo confronta con quello specificato nel campo hash del blocco.
Il metodo stringa GetTxsHash() restituisce l'hash totale di tutte le transazioni nel blocco.
Il metodo GetMerkleRoot() specifica la radice dell'albero Merkle per le transazioni in un blocco.
Il metodo Sign(privk string) firma un blocco con la chiave privata del creatore del blocco.
Il metodo SetHeight(height int) scrive l'altezza del blocco nel campo della struttura del blocco.
Il metodo GetHeight() int restituisce l'altezza del blocco come specificato nel campo corrispondente della struttura del blocco.
Il metodo ToGOBBytes() []byte codifica un blocco in formato GOB e lo restituisce come una sezione di byte.
Il metodo di errore FromGOBBytes(data []byte) scrive i dati del blocco nella struttura a blocchi dalla sezione di byte passata in formato GOB.
Il metodo stringa GetHash() restituisce l'hash del blocco specificato.
Il metodo stringa GetPrevHash() restituisce l'hash del blocco precedente.
Il metodo SetPublicKey(pubk string) scrive la chiave pubblica del creatore del blocco nel blocco.
Pertanto, utilizzando i metodi dell'oggetto Block, possiamo facilmente convertirlo in un formato per la trasmissione in rete e il salvataggio nel database LevelDB.
Le funzioni del pacchetto blockchain sono responsabili del salvataggio sulla blockchain:
Per fare ciΓ², 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 volta quando il pacchetto viene inizializzato nella funzione init():
db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).
CloseDB() Γ¨ un wrapper per db.Cloce(), chiamato dopo aver lavorato con le funzioni del pacchetto per chiudere la connessione al database.
La funzione di errore SetTargetBlockHash(stringa hash) scrive nel database l'hash del blocco corrente con la chiave specificata dalla costante BLOCK_HASH.
La funzione GetTargetBlockHash() (stringa, errore) restituisce l'hash del blocco corrente memorizzato nel database.
La funzione di errore SetTargetBlockHeight(height int) scrive nel database il valore dell'altezza blockchain per il nodo con la chiave specificata dalla costante BLOCK_HEIGHT.
La funzione GetTargetBlockHeight() (int, errore) restituisce l'altezza della blockchain per un dato nodo, memorizzata nel database.
La funzione bool CheckBlock(block IBlock) controlla la correttezza di un blocco prima di aggiungere questo blocco alla blockchain.
La funzione di errore AddBlock(block IBlock) aggiunge un blocco alla blockchain.
Le funzioni per recuperare e visualizzare i blocchi si trovano nel file explore.go del pacchetto blockchain:
La funzione GetBlockByHash(hash string) (*block.Block, errore) crea un oggetto blocco vuoto, carica al suo interno un blocco dal database, il cui hash gli Γ¨ stato passato e restituisce un puntatore ad esso.
La creazione di un blocco Genesis viene eseguita dalla funzione di errore Genesis() dal file genesis.go del pacchetto blockchain.
Il prossimo articolo parlerΓ della connessione dei client a un nodo utilizzando il meccanismo WebSocket.
Fonte: habr.com