Um letztendlich eine Blockchain und nicht nur eine Datenbank zu erhalten, müssen wir drei wichtige Elemente in unser Projekt hinzufügen:
- Beschreibung der Datenstruktur und der Methoden des Blocks
- Beschreibung der Datenstruktur und der Methoden der Transaktion
- Die Funktionen der Blockchain, die Blöcke in der Datenbank speichern und diese anhand ihres Hashes oder ihrer Höhe (oder auf andere Weise) finden.

Dies ist der zweite Artikel über Blockchain für die Industrie, der erste .
Wenn ich an die Fragen denke, die mir von Lesern zu dem vorherigen Artikel dieser Reihe gestellt wurden, sollte ich erwähnen: Für die Speicherung der Blockchain-Daten wird in diesem Fall die LevelDB-Datenbank verwendet, aber nichts hindert daran, eine andere zu verwenden, sagen wir MySQL. Lassen Sie uns jetzt die Struktur dieser Daten untersuchen.
Lassen Sie uns mit den Transaktionen beginnen:
Hier ist ihre Datenstruktur:
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 werden der Datentyp (für Transaktion 2), der Hash dieser Transaktion, der Typ der Transaktion, der Zeitstempel sowie die Eingaben und Ausgaben gespeichert. Die Eingaben TxIn speichern den Hash der Transaktion, auf die verwiesen wird, die Nummer dieser Ausgabe und den Bytecode, während die Ausgaben TxOut einen bestimmten Wert und ebenfalls Bytecode speichern.
Lassen Sie uns nun ansehen, welche Aktionen eine Transaktion mit ihren Daten durchführen kann, das heißt, lassen Sie uns die Methoden untersuchen.
Die Funktion transaction.NewTransaction(txtype byte) *TX dient zur Erstellung einer Transaktion.
Die Methode AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) fügt der Transaktion einen Eingang hinzu.
Die Methode AddTxOut(value int, data []byte) (*TxOut, error) fügt der Transaktion einen Ausgang hinzu.
Die Methode ToBytes() []byte verwandelt die Transaktion in einen Byte-Slice.
Die interne Funktion preByteHash(bytes []byte) string wird in Build() und Check() verwendet, um die erstellte Hash der Transaktionen mit den Hashes der Transaktionen, die von JavaScript-Anwendungen generiert werden, kompatibel zu machen.
Die Methode Build() legt den Hash der Transaktion wie folgt fest: tx.TxHash = preByteHash(tx.ToBytes()).
Die Methode ToJSON() string wandelt die Transaktion in einen JSON-String um.
Die Methode FromJSON(data []byte) error lädt die Transaktion aus dem JSON-Format, das als Byte-Slice übergeben wird.
Die Methode Check() bool vergleicht den erhaltenen Hash aus dem Feld des Transaktionshashes mit dem Hash, der durch Hashing dieser Transaktion (ohne Berücksichtigung des Hashfeldes) erhalten wurde.
Transaktionen werden in den Block hinzugefügt:
Die Datenstruktur des Blocks ist umfangreicher:
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 speichert den Datentyp, anhand dessen der Knoten den Block von einer Transaktion oder anderen Daten unterscheidet. Für den Block hat dieser Wert den Wert 1.
BlockHeight speichert die Höhe des Blocks.
Timestamp ist der Zeitstempel.
HeaderSize ist die Größe des Blocks in Bytes.
PrevBlockHash ist der Hash des vorherigen Blocks, und SelfBlockHash ist der aktuelle Hash.
TxsHash ist der Gesamt-Hash der Transaktionen.
MerkleRoot ist die Wurzel des Merkle-Baums.
Im Folgenden befinden sich die öffentlichen Schlüssel des Blockerstellers, die Signatur des Erstellers, die Blockversion, die Anzahl der Transaktionen im Block und diese Transaktionen selbst.
Lassen Sie uns seine Methoden betrachten:
Zur Erstellung eines Blocks wird die Funktion block.NewBlock() verwendet: NewBlock(prevBlockHash string, height int) *Block, die den Hash des vorherigen Blocks und die Höhe, die für den zu erstellenden Block in der Blockchain festgelegt ist, entgegennimmt. Zudem wird der Blocktyp aus der Konstante des Pakets types festgelegt:
b.DataType = types.BLOCK_TYPE.Die Methode AddTx(tx *transaction.TX) fügt eine Transaktion in den Block ein.
Die Methode Build() lädt Werte in die Felder des Blocks und generiert und setzt seinen aktuellen Hash.
Die Methode ToBytesHeader() []byte konvertiert den Blockheader (ohne Transaktionen) in einen Byte-Slice.
Die Methode ToJSON() string konvertiert den Block in das JSON-Format im String-Datenformat.
Die Methode FromJSON(data []byte) error lädt Daten aus JSON in die Blockstruktur.
Die Methode Check() bool generiert den Hash des Blocks und vergleicht ihn mit dem im Blockhash-Feld angegebenen.
Die Methode GetTxsHash() string gibt den Gesamt-Hash aller Transaktionen im Block zurück.
Die Methode GetMerkleRoot() setzt die Wurzel des Merkle-Baums für die Transaktionen im Block.
Die Methode Sign(privk string) signiert den Block mit dem privaten Schlüssel des Blockerstellers.
Die Methode SetHeight(height int) speichert die Höhe des Blocks im Feld der Blockstruktur.
Die Methode GetHeight() int gibt die Höhe des Blocks zurück, wie sie im entsprechenden Feld der Blockstruktur angegeben ist.
Die Methode ToGOBBytes() []byte kodiert den Block im GOB-Format und gibt ihn als Byte-Slice zurück.
Die Methode FromGOBBytes(data []byte) error schreibt die Blockdaten in die Blockstruktur aus dem übergebenen Byte-Slice im GOB-Format.
Die Methode GetHash() string gibt den Hash des aktuellen Blocks zurück.
Die Methode GetPrevHash() string gibt den Hash des vorherigen Blocks zurück.
Die Methode SetPublicKey(pubk string) speichert den öffentlichen Schlüssel des Blockerstellers im Block.
Durch die Methoden des Block-Objekts können wir es also problemlos in ein Netzwerkübertragungsformat und zur Speicherung in einer LevelDB-Datenbank konvertieren.
Für die Speicherung in der Blockchain sind die Funktionen des Blockchain-Pakets verantwortlich:
Dafür muss der Block das Interface IBlock implementieren:
type IGOBBytes interface {
ToGOBBytes() []byte
FromGOBBytes(data []byte) error
}
type IBlock interface {
IGOBBytes
GetHash() string
GetPrevHash() string
GetHeight() int
Check() bool
}Die Verbindung zur Datenbank wird einmalig beim Initialisieren des Pakets in der Funktion init() hergestellt:
db, err = leveldb.OpenFile(BLOCKCHAIN_DB_DEBUG, nil).CloseDB() ist eine Wrapper-Funktion für db.Close() — sie wird nach der Arbeit mit den Funktionen des Pakets aufgerufen, um die Verbindung zur DB zu schließen.
Die Funktion SetTargetBlockHash(hash string) error schreibt den Hash des aktuellen Blocks in die DB mit dem durch die Konstante BLOCK_HASH festgelegten Schlüssel.
Die Funktion GetTargetBlockHash() (string, error) gibt den Hash des aktuellen Blocks zurück, der in der DB gespeichert ist.
Die Funktion SetTargetBlockHeight(height int) error schreibt den Wert der Höhe der Blockchain für den Knoten in die DB mit dem durch die Konstante BLOCK_HEIGHT festgelegten Schlüssel.
Die Funktion GetTargetBlockHeight() (int, error) gibt die Höhe der Blockchain für diesen Knoten zurück, die in der DB gespeichert ist.
Die Funktion CheckBlock(block IBlock) bool überprüft die Korrektheit des Blocks, bevor dieser zur Blockchain hinzugefügt wird.
Die Funktion AddBlock(block IBlock) error fügt einen Block zur Blockchain hinzu.
Die Funktionen zum Abrufen und Anzeigen von Blöcken befinden sich in der Datei explore.go des Blockchain-Pakets:
Die Funktion GetBlockByHash(hash string) (*block.Block, error) erstellt ein leeres Blockobjekt, lädt den Block mit dem angegebenen Hash aus der DB und gibt einen Zeiger darauf zurück.
Die Erstellung des Genesis-Blocks erfolgt in der Funktion Genesis() error in der Datei genesis.go des Blockchain-Pakets.
Im nächsten Artikel geht es um die Verbindung zu Client-Knoten mittels des WebSocket-Mechanismus.
Quelle: habr.com
