Como deseñei bloques e transaccións na miña cadea de bloques Go

Para acabar cunha cadea de bloques e non só cunha base de datos, necesitamos engadir 3 elementos importantes ao noso proxecto:

  • Descrición da estrutura e métodos de datos do bloque
  • Descrición da estrutura de datos e métodos de transacción
  • Funcións de cadea de bloques que gardan bloques nunha base de datos e atopalos alí polo seu hash ou altura (ou outra cousa).

Como deseñei bloques e transaccións na miña cadea de bloques Go

Este é o segundo artigo sobre blockchain para a industria, o primeiro aquí.

Recordando as preguntas que os lectores me fixeron sobre o artigo anterior desta serie, hai que ter en conta: neste caso, a base de datos LevelDB utilízase para almacenar datos da cadea de bloques, pero nada impide que use calquera outro, por exemplo, MySQL. Agora vexamos a estrutura destes datos.

Comecemos coas transaccións: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Aquí está a súa estrutura 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
}

TX almacena o tipo de datos (para a transacción 2), o hash desa transacción, o tipo da transacción en si, unha marca de tempo e entradas e saídas. As entradas TxIn almacenan o hash da transacción cuxa saída se fai referencia, o número desta saída e o bytecode, e as saídas TxOut almacenan algún valor e tamén bytecode.

Agora vexamos que accións pode realizar unha transacción sobre os seus datos, é dicir. Vexamos os métodos.

Para crear unha transacción, use a función transaction.NewTransaction(txtype byte) *TX.

O método AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, erro) engade unha entrada á transacción.

O método AddTxOut(valor int, datos []byte) (*TxOut, erro) engade unha saída á transacción.

O método ToBytes() []byte converte a transacción nunha porción de bytes.

A cadea de función interna preByteHash(bytes []byte) úsase en Build() e Check() para facer que o hash de transacción xerado sexa compatible cos hash de transacción xerados desde aplicacións JavaScript.

O método Build() establece o hash da transacción do seguinte xeito: tx.TxHash = preByteHash(tx.ToBytes()).

O método de cadea ToJSON() converte unha transacción nunha cadea JSON.

O método de erro FromJSON(data []byte) carga unha transacción do formato JSON que se pasa como un segmento de bytes.

O método bool Check() compara o hash resultante do campo hash da transacción co hash obtido como resultado do hash desta transacción (ignorando o campo hash).

As transaccións engádense ao bloque: github.com/Rusldv/bcstartup/blob/master/block/builder.go

A estrutura de datos do bloque é máis 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 almacena o tipo de datos, o nodo utilízao e distingue o bloque dunha transacción ou doutros datos. Para un bloque este valor é 1.

BlockHeight almacena a altura do bloque.
Marca de tempo marca de tempo.
HeaderSize é o tamaño do bloque en bytes.
PrevBlockHash é o hash do bloque anterior e SelfBlockHash é o hash do actual.
TxsHash é un hash xeral de transaccións.
MerkleRoot é a raíz da árbore Merkle.

Ademais, nos campos hai a clave pública do creador do bloque, a sinatura do creador, a versión do bloque, o número de transaccións no bloque e estas transaccións en si.

Vexamos os seus métodos:
Para crear un bloque, use a función block.NewBlock(): NewBlock(prevBlockHash string, height int) *Bloque, que toma o hash do bloque anterior e a altura establecida para o bloque creado na cadea de bloques. O tipo de bloque tamén se define a partir da constante do paquete de tipos:

b.DataType = types.BLOCK_TYPE.

O método AddTx(tx *transaction.TX) engade unha transacción a un bloque.

O método Build() carga valores nos campos do bloque e xera e establece o seu hash actual.

O método ToBytesHeader() []byte converte a cabeceira do bloque (sen transaccións) nunha porción de bytes.

O método de cadea ToJSON() converte o bloque a formato JSON nunha representación de cadea dos datos.

O método de erro FromJSON(data []byte) carga datos de JSON nunha estrutura de bloques.

O método bool Check() xera un bloque hash e compárao co especificado no campo hash de bloque.

O método de cadea GetTxsHash() devolve o hash total de todas as transaccións do bloque.

O método GetMerkleRoot() especifica a raíz da árbore de Merkle para as transaccións nun bloque.

O método Sign(string privk) asina un bloque coa clave privada do creador do bloque.

O método SetHeight(height int) escribe a altura do bloque no campo da estrutura do bloque.

O método GetHeight() int devolve a altura do bloque tal e como se especifica no campo correspondente da estrutura do bloque.

O método ToGOBBytes() []byte codifica un bloque en formato GOB e devólveo como un segmento de bytes.

O método de erro FromGOBBytes(data []byte) escribe os datos do bloque na estrutura do bloque desde o segmento de byte pasado en formato GOB.

O método de cadea GetHash() devolve o hash do bloque indicado.

O método de cadea GetPrevHash() devolve o hash do bloque anterior.

O método SetPublicKey(cadea pubk) escribe a chave pública do creador do bloque no bloque.

Así, usando os métodos do obxecto Block, podemos convertelo facilmente nun formato para transmitilo pola rede e gardar na base de datos LevelDB.

As funcións do paquete blockchain son as encargadas de gardar na cadea de bloques: github.com/Rusldv/bcstartup/tree/master/blockchain

Para iso, o bloque debe implementar a interface IBlock:

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

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

}

A conexión á base de datos créase unha vez cando se inicializa o paquete na función init():

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

CloseDB() é un envoltorio para db.Cloce() - chamado despois de traballar coas funcións do paquete para pechar a conexión coa base de datos.

A función de erro SetTargetBlockHash(cadea hash) escribe o hash do bloque actual coa clave especificada pola constante BLOCK_HASH na base de datos.

A función GetTargetBlockHash() (cadea, erro) devolve o hash do bloque actual almacenado na base de datos.

A función de erro SetTargetBlockHeight(height int) escribe na base de datos o valor da altura da cadea de bloques para o nodo coa clave especificada pola constante BLOCK_HEIGHT.

A función GetTargetBlockHeight() (int, erro) devolve a altura da cadea de bloques para un determinado nodo, almacenada na base de datos.

A función bool CheckBlock(block IBlock) comproba a corrección dun bloque antes de engadir este bloque á cadea de bloques.

A función de erro AddBlock (bloque IBlock) engade un bloque á cadea de bloques.

As funcións para recuperar e ver bloques están no ficheiro explore.go do paquete blockchain:

A función GetBlockByHash(cadea hash) (*block.Block, erro) crea un obxecto de bloque baleiro, carga nel desde a base de datos, cuxo hash se lle pasou e devolve un punteiro a el.

A creación dun bloque de xénese realízase pola función de erro Genesis() do ficheiro genesis.go do paquete blockchain.

O seguinte artigo falarase sobre a conexión de clientes a un nodo mediante o mecanismo WebSocket.

Fonte: www.habr.com

Engadir un comentario