Como eu projetei blocos e transações no My Go Blockchain

Para acabarmos com um blockchain e não apenas com um banco de dados, precisamos adicionar 3 elementos importantes ao nosso projeto:

  • Descrição da estrutura e métodos de dados do bloco
  • Descrição da estrutura de dados e métodos de transação
  • Funções Blockchain que salvam blocos em um banco de dados e os encontram por hash ou altura (ou qualquer outra coisa).

Como eu projetei blocos e transações no My Go Blockchain

Este é o segundo artigo sobre blockchain para a indústria, o primeiro aqui.

Lembrando as perguntas que os leitores me fizeram sobre o artigo anterior desta série, cabe ressaltar: neste caso, o banco de dados LevelDB é usado para armazenar dados blockchain, mas nada impede que você use qualquer outro, digamos, MySQL. Agora vamos dar uma olhada na estrutura desses dados.

Vamos começar com as transações: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

Aqui está sua estrutura de dados:

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 armazena o tipo de dados (para a transação 2), o hash dessa transação, o tipo da transação em si, um carimbo de data/hora e entradas e saídas. As entradas TxIn armazenam o hash da transação cuja saída é referenciada, o número desta saída e o bytecode, e as saídas TxOut armazenam algum valor e também o bytecode.

Agora vamos ver quais ações uma transação pode realizar em seus dados, ou seja, Vejamos os métodos.

Para criar uma transação, use a função transaction.NewTransaction(txtype byte) *TX.

O método AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) adiciona uma entrada à transação.

O método AddTxOut(value int, data []byte) (*TxOut, error) adiciona uma saída à transação.

O método ToBytes() []byte transforma a transação em uma fatia de bytes.

A função interna preByteHash(bytes []byte) string é usada em Build() e Check() para tornar o hash de transação gerado compatível com hashes de transação gerados a partir de aplicativos JavaScript.

O método Build() define o hash da transação da seguinte forma: tx.TxHash = preByteHash(tx.ToBytes()).

O método de string ToJSON() converte uma transação em uma string JSON.

O método de erro FromJSON(data []byte) carrega uma transação do formato JSON passado como uma fatia de bytes.

O método bool Check() compara o hash resultante do campo hash da transação com o hash obtido como resultado do hash desta transação (ignorando o campo hash).

As transações são adicionadas ao bloco: github.com/Rusldv/bcstartup/blob/master/block/builder.go

A estrutura de dados do bloco é mais volumosa:

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 armazena o tipo de dados, o nó o utiliza e distingue o bloco de uma transação ou outros dados. Para um bloco esse valor é 1.

BlockHeight armazena a altura do bloco.
Carimbo de data e hora.
HeaderSize é o tamanho do bloco em bytes.
PrevBlockHash é o hash do bloco anterior e SelfBlockHash é o hash do bloco atual.
TxsHash é um hash geral de transações.
MerkleRoot é a raiz da árvore Merkle.

Mais adiante nos campos estão a chave pública do criador do bloco, a assinatura do criador, a versão do bloco, o número de transações no bloco e as próprias transações.

Vejamos seus métodos:
Para criar um bloco, use a função block.NewBlock(): NewBlock(prevBlockHash string, height int) *Block, que pega o hash do bloco anterior e a altura definida para o bloco criado no blockchain. O tipo de bloco também é definido na constante do pacote types:

b.DataType = types.BLOCK_TYPE.

O método AddTx(tx *transaction.TX) adiciona uma transação a um bloco.

O método Build() carrega valores nos campos do bloco e gera e define seu hash atual.

O método ToBytesHeader() []byte converte o cabeçalho do bloco (sem transações) em uma fatia de bytes.

O método de string ToJSON() converte o bloco para o formato JSON em uma representação de string dos dados.

O método de erro FromJSON(data []byte) carrega dados de JSON em uma estrutura de bloco.

O método bool Check() gera um hash de bloco e o compara com aquele especificado no campo hash de bloco.

O método de string GetTxsHash() retorna o hash total de todas as transações no bloco.

O método GetMerkleRoot() especifica a raiz da árvore Merkle para transações em um bloco.

O método Sign(privk string) assina um bloco com a chave privada do criador do bloco.

O método SetHeight(height int) grava a altura do bloco no campo de estrutura do bloco.

O método GetHeight() int retorna a altura do bloco conforme especificado no campo correspondente da estrutura do bloco.

O método ToGOBBytes() []byte codifica um bloco no formato GOB e o retorna como uma fatia de bytes.

O método de erro FromGOBBytes(data []byte) grava dados de bloco na estrutura de bloco a partir da fatia de bytes passada no formato GOB.

O método de string GetHash() retorna o hash do bloco fornecido.

O método de string GetPrevHash() retorna o hash do bloco anterior.

O método SetPublicKey(pubk string) grava a chave pública do criador do bloco no bloco.

Assim, utilizando os métodos do objeto Block, podemos facilmente convertê-lo em um formato para transmissão pela rede e salvamento no banco de dados LevelDB.

As funções do pacote blockchain são responsáveis ​​por salvar no blockchain: github.com/Rusldv/bcstartup/tree/master/blockchain

Para fazer isso, o bloco deve 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 conexão com o banco de dados é criada uma vez quando o pacote é inicializado na função init():

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

CloseDB() é um wrapper para db.Cloce() - chamado após trabalhar com as funções do pacote para fechar a conexão com o banco de dados.

A função de erro SetTargetBlockHash(hash string) grava o hash do bloco atual com a chave especificada pela constante BLOCK_HASH no banco de dados.

A função GetTargetBlockHash() (string, error) retorna o hash do bloco atual armazenado no banco de dados.

A função de erro SetTargetBlockHeight(height int) grava no banco de dados o valor da altura do blockchain para o nó com a chave especificada pela constante BLOCK_HEIGHT.

A função GetTargetBlockHeight() (int, error) retorna a altura do blockchain para um determinado nó, armazenado no banco de dados.

A função bool CheckBlock(block IBlock) verifica a correção de um bloco antes de adicionar este bloco ao blockchain.

A função de erro AddBlock(block IBlock) adiciona um bloco ao blockchain.

As funções para recuperar e visualizar blocos estão no arquivo explore.go do pacote blockchain:

A função GetBlockByHash(hash string) (*block.Block, error) cria um objeto de bloco vazio, carrega nele um bloco do banco de dados, cujo hash foi passado para ele, e retorna um ponteiro para ele.

A criação de um bloco genesis é realizada pela função de erro Genesis() do arquivo genesis.go do pacote blockchain.

O próximo artigo falará sobre como conectar clientes a um nó usando o mecanismo WebSocket.

Fonte: habr.com

Adicionar um comentário