Jak zaprojektowałem bloki i transakcje w moim blockchainie Go

Aby ostatecznie otrzymać blockchain, a nie tylko bazę danych, musimy do naszego projektu dodać 3 ważne elementy:

  • Opis struktury danych blokowych i metod
  • Opis struktury danych i metod transakcji
  • Funkcje Blockchain, które zapisują bloki w bazie danych i odnajdują je tam według ich skrótu lub wysokości (lub czegoś innego).

Jak zaprojektowałem bloki i transakcje w moim blockchainie Go

To już drugi artykuł o blockchainie dla przemysłu, pierwszy tutaj.

Pamiętając pytania, jakie zadali mi czytelnicy na temat poprzedniego artykułu z tej serii, warto zauważyć: w tym przypadku baza LevelDB służy do przechowywania danych typu blockchain, ale nic nie stoi na przeszkodzie, aby skorzystać z innego, powiedzmy MySQL. Przyjrzyjmy się teraz strukturze tych danych.

Zacznijmy od transakcji: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Oto jego struktura danych:

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 przechowuje typ danych (dla transakcji 2), skrót tej transakcji, typ samej transakcji, znacznik czasu oraz dane wejściowe i wyjściowe. Wejścia TxIn przechowują skrót transakcji, do której wyjścia się odwołujemy, numer tego wyjścia i kod bajtowy, a wyjścia TxOut przechowują pewną wartość, a także kod bajtowy.

Zobaczmy teraz, jakie działania może wykonać transakcja na swoich danych, tj. Spójrzmy na metody.

Aby utworzyć transakcję, użyj funkcji transakcja.NewTransaction(txtype byte) *TX.

Metoda AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) dodaje dane wejściowe do transakcji.

Metoda AddTxOut(value int, data []byte) (*TxOut, error) dodaje dane wyjściowe do transakcji.

Metoda ToBytes() []byte zamienia transakcję w wycinek bajtu.

Funkcja wewnętrzna preByteHash(bytes []byte) string jest używana w funkcjach Build() i Check() w celu zapewnienia zgodności wygenerowanego skrótu transakcji ze skrótami transakcji generowanymi z aplikacji JavaScript.

Metoda Build() ustawia skrót transakcji w następujący sposób: tx.TxHash = preByteHash(tx.ToBytes()).

Metoda łańcuchowa ToJSON() konwertuje transakcję na ciąg JSON.

Metoda błędu FromJSON(data []byte) ładuje transakcję z formatu JSON przekazanego jako wycinek bajtu.

Metoda bool Check() porównuje wynikowy hash z pola hash transakcji z hashem uzyskanym w wyniku hashowania tej transakcji (ignorując pole hash).

Do bloku dodawane są transakcje: github.com/Rusldv/bcstartup/blob/master/block/builder.go

Struktura danych blokowych jest bardziej obszerna:

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 przechowuje typ danych, węzeł go wykorzystuje i odróżnia blok od transakcji lub innych danych. Dla bloku ta wartość wynosi 1.

BlockHeight przechowuje wysokość bloku.
Znacznik czasu.
HeaderSize to rozmiar bloku w bajtach.
PrevBlockHash to skrót poprzedniego bloku, a SelfBlockHash to skrót bieżącego bloku.
TxsHash to ogólny skrót transakcji.
MerkleRoot jest korzeniem drzewa Merkle.

W dalszej części pól znajduje się klucz publiczny twórcy bloku, podpis twórcy, wersja bloku, liczba transakcji w bloku oraz same te transakcje.

Przyjrzyjmy się jego metodom:
Aby utworzyć blok, użyj funkcji block.NewBlock(): NewBlock(prevBlockHash string, wysokość int) *Block, która pobiera hash poprzedniego bloku i wysokość ustawioną dla utworzonego bloku w łańcuchu bloków. Typ bloku jest również ustawiany na podstawie stałej pakietu types:

b.DataType = types.BLOCK_TYPE.

Metoda AddTx(tx *transaction.TX) dodaje transakcję do bloku.

Metoda Build() ładuje wartości do pól bloku oraz generuje i ustawia jego aktualny hash.

Metoda ToBytesHeader() []byte konwertuje nagłówek bloku (bez transakcji) na wycinek bajtu.

Metoda łańcuchowa ToJSON() konwertuje blok do formatu JSON w postaci ciągu znaków reprezentującego dane.

Metoda błędu FromJSON(data []byte) ładuje dane z formatu JSON do struktury blokowej.

Metoda bool Check() generuje skrót bloku i porównuje go z hashem określonym w polu skrótu bloku.

Metoda łańcuchowa GetTxsHash() zwraca całkowity skrót wszystkich transakcji w bloku.

Metoda GetMerkleRoot() określa korzeń drzewa Merkle dla transakcji w bloku.

Metoda Sign(privk string) podpisuje blok kluczem prywatnym twórcy bloku.

Metoda SetHeight(height int) zapisuje wysokość bloku w polu struktury bloku.

Metoda int GetHeight() zwraca wysokość bloku określoną w odpowiednim polu struktury bloku.

Metoda ToGOBBytes() []byte koduje blok w formacie GOB i zwraca go jako wycinek bajtu.

Metoda błędu FromGOBBytes(data []byte) zapisuje dane blokowe do struktury blokowej z przekazanego wycinka bajtu w formacie GOB.

Metoda string GetHash() zwraca hash danego bloku.

Metoda łańcuchowa GetPrevHash() zwraca skrót poprzedniego bloku.

Metoda SetPublicKey(pubk string) zapisuje do bloku klucz publiczny twórcy bloku.

Tym samym, korzystając z metod obiektu Block, możemy łatwo przekonwertować go na format umożliwiający transmisję przez sieć i zapis do bazy LevelDB.

Funkcje pakietu blockchain odpowiadają za zapisywanie do blockchaina: github.com/Rusldv/bcstartup/tree/master/blockchain

Aby to zrobić, blok musi implementować interfejs IBlock:

type IGOBBytes interface {
	ToGOBBytes() []byte
	FromGOBBytes(data []byte) error
}

type IBlock interface {
	IGOBBytes
	GetHash() string
	GetPrevHash() string
	GetHeight() int
	Check() bool

}

Połączenie z bazą danych jest tworzone jednorazowo podczas inicjowania pakietu w funkcji init():

db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).

CloseDB() to opakowanie dla db.Cloce() - wywoływane po pracy z funkcjami pakietu w celu zamknięcia połączenia z bazą danych.

Funkcja błędu SetTargetBlockHash(hash string) zapisuje do bazy danych hash bieżącego bloku z kluczem określonym przez stałą BLOCK_HASH.

Funkcja GetTargetBlockHash() (string, błąd) zwraca hash bieżącego bloku przechowywanego w bazie danych.

Funkcja błędu SetTargetBlockHeight(height int) zapisuje do bazy danych wartość wysokości blockchainu dla węzła z kluczem określonym przez stałą BLOCK_HEIGHT.

Funkcja GetTargetBlockHeight() (int, error) zwraca wysokość blockchainu dla danego węzła, przechowywanego w bazie danych.

Funkcja bool CheckBlock(block IBlock) sprawdza poprawność bloku przed dodaniem go do łańcucha bloków.

Funkcja błędu AddBlock(block IBlock) dodaje blok do łańcucha bloków.

Funkcje pobierania i przeglądania bloków znajdują się w pliku explore.go pakietu blockchain:

Funkcja GetBlockByHash(hash string) (*block.Block, error) tworzy pusty obiekt blokowy, ładuje do niego blok z bazy danych, której hash został do niego przekazany, i zwraca do niego wskaźnik.

Tworzenie bloku genesis odbywa się za pomocą funkcji błędu Genesis() z pliku genesis.go pakietu blockchain.

W następnym artykule omówione zostanie połączenie z węzłem klienckim za pomocą mechanizmu WebSocket.

Źródło: www.habr.com

Dodaj komentarz