Come ho progettato blocchi e transazioni nella mia blockchain Go

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).

Come ho progettato blocchi e transazioni nella mia blockchain Go

Questo Γ¨ il secondo articolo sulla blockchain per l’industria, il primo qui.

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: 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
}

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: github.com/Rusldv/bcstartup/blob/master/block/builder.go

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: github.com/Rusldv/bcstartup/tree/master/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

Aggiungi un commento