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).
To już drugi artykuł o blockchainie dla przemysłu, pierwszy
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:
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:
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:
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