Come ho progettato i blocchi e le transazioni nel mio blockchain in Go

Per ottenere un blockchain e non semplicemente un database, è necessario aggiungere al proprio 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 database e li cercano lì tramite l'hash o l'altezza (o in altro modo).

Come ho progettato i blocchi e le transazioni nel mio blockchain in Go

Questo è il secondo articolo sul blockchain per l'industria, il primo qui.

Ricordando le domande che i lettori mi hanno posto riguardo al precedente articolo di questo ciclo, va notato che per memorizzare i dati del blockchain in questo caso viene utilizzato il database LevelDB, ma non c'è nulla che impedisca di usare qualsiasi altro, ad esempio MySQL. Adesso analizziamo la struttura di questi dati.

Iniziamo con le transazioni: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

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 i tipi di dati (per la transazione 2), l'hash di questa transazione, il tipo della transazione stessa, il timestamp, oltre agli input e agli output. Gli input TxIn contengono l'hash della transazione a cui si riferiscono, il numero di questo output e il bytecode, mentre gli output TxOut memorizzano un valore e anch'esso bytecode.

Adesso vediamo quali azioni può compiere una transazione sui propri dati, ovvero analizziamo i metodi.

La funzione che serve a creare una transazione è transaction.NewTransaction(txtype byte) *TX.

Il metodo AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) 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 un array di byte.

La funzione interna preByteHash(bytes []byte) string è utilizzata in Build() e Check() per garantire la compatibilità dell'hash delle transazioni create con gli hash delle transazioni generate da applicazioni in JavaScript.

Il metodo Build() imposta l'hash della transazione nel seguente modo: tx.TxHash = preByteHash(tx.ToBytes()).

Il metodo ToJSON() string trasforma 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 hash della transazione con l'hash ottenuto dal hashing di questa transazione (escludendo il campo hash).

Le transazioni vengono aggiunte al blocco: github.com/Rusldv/bcstartup/blob/master/block/builder.go

La struttura dei dati del blocco è più complessa:

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, consentendo al nodo di distinguere tra un blocco, una transazione o altri dati. Per un blocco, questo valore è pari a 1.

BlockHeight memorizza l'altezza del blocco.
Timestamp rappresenta la marca temporale.
HeaderSize indica la dimensione del blocco in byte.
PrevBlockHash è l'hash del blocco precedente, mentre SelfBlockHash è quello attuale.
TxsHash è l'hash totale delle transazioni.
MerkleRoot è la radice dell'albero di Merkle.

Successivamente, nei campi ci sono la chiave pubblica del creatore del blocco, la firma del creatore, la versione del blocco, il numero di transazioni nel blocco e le rispettive transazioni.

Esaminiamo i suoi metodi:
Per creare un blocco si usa 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 inoltre specificato 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 attuale hash.

Il metodo ToBytesHeader() []byte converte l'intestazione del blocco (senza transazioni) in uno slice di byte.

Il metodo ToJSON() string converte il blocco nel formato JSON nella 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 complessivo 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 come specificato nel campo corrispondente della struttura del blocco.

Il metodo ToGOBBytes() []byte codifica un blocco nel formato GOB e lo restituisce come un array di byte.

Il metodo FromGOBBytes(data []byte) error scrive i dati del blocco nella struttura del blocco dall'array di byte fornito nel 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) scrive nel blocco la chiave pubblica del creatore del blocco.

In questo modo, grazie ai metodi dell'oggetto Block, possiamo facilmente convertirlo nel formato per la trasmissione in rete e per il salvataggio nel database LevelDB.

Le funzioni del pacchetto blockchain si occupano del salvataggio nella blockchain: github.com/Rusldv/bcstartup/tree/master/blockchain

Per questo 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 è 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 aver lavorato con le funzioni del pacchetto per chiudere la connessione al DB.

La funzione SetTargetBlockHash(hash string) error scrive nel DB l'hash del blocco corrente con la chiave definita dalla costante BLOCK_HASH.

La funzione GetTargetBlockHash() (stringa, errore) restituisce l'hash dell'attuale blocco, memorizzato nel database.

La funzione SetTargetBlockHeight(height int) errore scrive nel database il valore dell'altezza della blockchain per il nodo con la chiave definita dalla costante BLOCK_HEIGHT.

La funzione GetTargetBlockHeight() (int, errore) restituisce l'altezza della blockchain per questo nodo, memorizzata nel database.

La funzione CheckBlock(block IBlock) bool esegue un controllo del blocco per verificarne la correttezza prima di aggiungere il blocco alla blockchain.

La funzione AddBlock(block IBlock) errore 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, errore) crea un oggetto blocco vuoto, carica il blocco dal database il cui hash è stato fornito e restituisce un puntatore ad esso.

La creazione del blocco genesi è effettuata dalla funzione Genesis() errore nel file genesis.go del pacchetto blockchain.

Nel prossimo articolo si parlerà della connessione ai nodi dei clienti attraverso il meccanismo WebSocket.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster