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).
Este é o segundo artigo sobre blockchain para a industria, o primeiro
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:
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:
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:
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