Cómo diseñé bloques y transacciones en mi blockchain en Go

Para que al final se obtenga una cadena de bloques y no solo una base de datos, necesitamos agregar a nuestro proyecto 3 elementos importantes:

  • Descripción de la estructura de datos y métodos del bloque
  • Descripción de la estructura de datos y métodos de la transacción
  • Funciones de la cadena de bloques que almacenan bloques en la base de datos y los encuentran allí según su hash o altura (o de alguna otra manera).

Cómo diseñé bloques y transacciones en mi blockchain en Go

Este es el segundo artículo sobre blockchain para la industria, el primero aquí.

Recordando las preguntas que me hicieron los lectores sobre el artículo anterior de esta serie, cabe señalar que para almacenar datos de la cadena de bloques en este caso se utiliza la base de datos LevelDB, pero nada impide usar cualquier otra, como MySQL. Ahora, profundicemos en la estructura de estos datos.

Comencemos con las transacciones: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Esta es su estructura de datos:

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
}

En TX se almacenan el tipo de datos (para la transacción 2), el hash de esta transacción, el tipo de la transacción, la marca de tiempo, así como las entradas y salidas. Las entradas TxIn almacenan el hash de la transacción a la que apuntan, el número de esta salida y el código de bytes, mientras que las salidas TxOut almacenan un valor y también un código de bytes.

Ahora veamos qué acciones puede realizar una transacción sobre sus datos, es decir, analicemos los métodos.

La función transaction.NewTransaction(txtype byte) *TX se utiliza para crear una transacción.

El método AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) agrega una entrada a la transacción.

El método AddTxOut(value int, data []byte) (*TxOut, error) agrega una salida a la transacción.

El método ToBytes() []byte convierte la transacción en un slice de bytes.

La función interna preByteHash(bytes []byte) string se aplica en Build() y Check() para la compatibilidad del hash de transacciones generado con los hashes de transacciones generados desde aplicaciones en JavaScript.

El método Build() establece el hash de la transacción de la siguiente manera: tx.TxHash = preByteHash(tx.ToBytes()).

El método ToJSON() string convierte la transacción en una cadena JSON.

El método FromJSON(data []byte) error carga la transacción desde el formato JSON, que se pasa como un slice de bytes.

El método Check() bool compara el hash recibido del campo hash de la transacción con el hash obtenido como resultado del hash de esta transacción (sin tener en cuenta el campo hash).

Las transacciones se agregan al bloque: github.com/Rusldv/bcstartup/blob/master/block/builder.go

La estructura de datos del bloque es más extensa:

tipo 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 almacena el tipo de datos, con el que el nodo diferencia un bloque de una transacción u otros datos. Para un bloque, este valor es igual a 1.

BlockHeight almacena la altura del bloque.
Timestamp es la marca de tiempo.
HeaderSize es el tamaño del bloque en bytes.
PrevBlockHash es el hash del bloque anterior, y SelfBlockHash — el actual.
TxsHash es el hash conjunto de las transacciones.
MerkleRoot es la raíz del árbol de Merkle.

En los siguientes campos se encuentran la clave pública del creador del bloque, la firma del creador, la versión del bloque, el número de transacciones en el bloque y, en sí, las transacciones.

Veamos sus métodos:
Para crear un bloque, se utiliza la función block.NewBlock(): NewBlock(prevBlockHash string, height int) *Block, que acepta el hash del bloque anterior y la altura establecida para el bloque creado en la cadena de bloques. También se especifica el tipo de bloque de la constante del paquete types:

b.DataType = types.BLOCK_TYPE.

El método AddTx(tx *transaction.TX) añade una transacción al bloque.

El método Build() carga los valores en los campos del bloque y genera y establece su hash actual.

El método ToBytesHeader() []byte convierte el encabezado del bloque (sin transacciones) en un slice de bytes.

El método ToJSON() string convierte el bloque en formato JSON en representación de cadena de datos.

El método FromJSON(data []byte) error carga datos desde JSON en la estructura del bloque.

El método Check() bool genera el hash del bloque y lo compara con el especificado en el campo del hash del bloque.

El método GetTxsHash() string devuelve el hash conjunto de todas las transacciones en el bloque.

El método GetMerkleRoot() establece la raíz del árbol de Merkle para las transacciones en el bloque.

El método Sign(privk string) firma el bloque con la clave privada del creador del bloque.

El método SetHeight(height int) registra la altura del bloque en el campo de la estructura del bloque.

El método GetHeight() int devuelve la altura del bloque tal como está indicada en el campo correspondiente de la estructura del bloque.

El método ToGOBBytes() []byte codifica el bloque en formato GOB y lo devuelve como un slice de bytes.

El método FromGOBBytes(data []byte) error registra los datos del bloque en la estructura del bloque desde el slice de bytes recibido en formato GOB.

El método GetHash() string devuelve el hash de este bloque.

El método GetPrevHash() string devuelve el hash del bloque anterior.

El método SetPublicKey(pubk string) registra en el bloque la clave pública del creador del bloque.

Así, mediante los métodos del objeto Block, podemos convertirlo fácilmente a un formato para su transmisión a través de la red y su almacenamiento en una base de datos LevelDB.

Las funciones del paquete blockchain se encargan de almacenar en la cadena de bloques: github.com/Rusldv/bcstartup/tree/master/blockchain

Para ello, el bloque debe implementar la interfaz IBlock:

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

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

}

La conexión a la base de datos se crea una vez durante la inicialización del paquete en la función init():

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

CloseDB() es un envoltorio para db.Close() — se llama después de trabajar con las funciones del paquete para cerrar la conexión con la base de datos.

La función SetTargetBlockHash(hash string) error graba en la base de datos el hash del bloque actual con la clave definida por la constante BLOCK_HASH.

La función GetTargetBlockHash() (string, error) devuelve el hash del bloque actual, almacenado en la base de datos.

La función SetTargetBlockHeight(height int) error graba en la base de datos el valor de la altura de la cadena de bloques para el nodo con la clave definida por la constante BLOCK_HEIGHT.

La función GetTargetBlockHeight() (int, error) devuelve la altura de la cadena de bloques para este nodo, almacenada en la base de datos.

La función CheckBlock(block IBlock) bool realiza una verificación del bloque antes de agregarlo a la cadena de bloques.

La función AddBlock(block IBlock) error añade un bloque a la cadena de bloques.

Las funciones para obtener y visualizar bloques se encuentran en el archivo explore.go del paquete blockchain:

La función GetBlockByHash(hash string) (*block.Block, error) crea un objeto de bloque vacío, carga el bloque desde la base de datos cuyo hash se le ha pasado y devuelve un puntero a él.

La creación del bloque génesis se lleva a cabo mediante la función Genesis() error del archivo genesis.go del paquete blockchain.

En el próximo artículo se hablará sobre cómo conectarse al nodo de los clientes mediante el mecanismo WebSocket.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster