我如何在 Go 区块链中设计区块和交易

为了最终得到一个区块链而不仅仅是一个数据库,我们需要向我们的项目添加 3 个重要元素:

  • 区块数据结构及方法说明
  • 数据结构及交易方式说明
  • 区块链功能将块保存在数据库中,并通过它们的哈希值或高度(或其他东西)找到​​它们。

我如何在 Go 区块链中设计区块和交易

这是关于行业区块链的第二篇文章,第一篇 这里.

还记得读者就本系列上一篇文章向我提出的问题吗?需要注意的是:在本例中,LevelDB 数据库用于存储区块链数据,但没有什么可以阻止您使用任何其他数据库,例如 MySQL。 现在让我们看看这个数据的结构。

让我们从交易开始: github.com/Rusldv/bcstartup/blob/master/transaction/builder.go

这是它的数据结构:

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 存储数据类型(对于交易 2)、该交易的哈希值、交易本身的类型、时间戳以及输入和输出。 TxIn 输入存储其输出被引用的交易的哈希值、该输出的编号和字节码,TxOut 输出存储一些值和字节码。

现在让我们看看事务可以对其数据执行哪些操作,即我们来看看方法。

要创建交易,请使用 transaction.NewTransaction(txtype byte) *TX 函数。

AddTxIn(thattxhash []byte, txoutn int, code []byte) (*TxIn, error) 方法向交易添加输入。

AddTxOut(value int, data []byte) (*TxOut, error) 方法向事务添加输出。

ToBytes() []byte 方法将事务转换为字节切片。

Build() 和 Check() 中使用内部函数 preByteHash(bytes []byte) string 来使生成的交易哈希与 JavaScript 应用程序生成的交易哈希兼容。

Build() 方法按如下方式设置交易哈希: tx.TxHash = preByteHash(tx.ToBytes())。

ToJSON() 字符串方法将交易转换为 JSON 字符串。

FromJSON(data []byte) 错误方法从作为字节切片传递的 JSON 格式加载事务。

Check() bool 方法将交易哈希字段产生的哈希值与对该交易进行哈希处理(忽略哈希字段)而获得的哈希值进行比较。

交易被添加到区块中: github.com/Rusldv/bcstartup/blob/master/block/builder.go

块数据结构更加庞大:

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 存储数据类型,节点使用它并将块与交易或其他数据区分开来。 对于块来说,该值为 1。

BlockHeight 存储块的高度。
时间戳 时间戳。
HeaderSize 是块大小(以字节为单位)。
PrevBlockHash 是前一个块的哈希值,SelfBlockHash 是当前块的哈希值。
TxsHash 是交易的通用哈希。
MerkleRoot 是 Merkle 树的根。

此外,在这些字段中还有区块创建者的公钥、创建者的签名、区块的版本、区块中的交易数量以及这些交易本身。

我们看一下它的方法:
要创建区块,请使用 block.NewBlock() 函数:NewBlock(prevBlockHash string, height int) *Block,它采用前一个区块的哈希值以及区块链中为创建的区块设置的高度。 块类型也是从 types 包常量设置的:

b.DataType = types.BLOCK_TYPE.

AddTx(tx *transaction.TX) 方法将交易添加到区块中。

Build() 方法将值加载到块的字段中,并生成并设置其当前哈希值。

ToBytesHeader() []byte 方法将块头(不含交易)转换为字节切片。

ToJSON() 字符串方法将块转换为数据字符串表示形式的 JSON 格式。

FromJSON(data []byte) 错误方法将数据从 JSON 加载到块结构中。

Check() bool 方法生成一个块哈希并将其与块哈希字段中指定的哈希值进行比较。

GetTxsHash() 字符串方法返回块中所有交易的总哈希值。

GetMerkleRoot() 方法指定块中交易的 Merkle 树的根。

Sign(privk string) 方法使用区块创建者的私钥对区块进行签名。

SetHeight(height int) 方法将块的高度写入块结构字段。

GetHeight() int 方法返回块结构的相应字段中指定的块高度。

ToGOBBytes() []byte 方法以 GOB 格式对块进行编码,并将其作为字节切片返回。

FromGOBBytes(data []byte) 错误方法将块数据以 GOB 格式从传递的字节片写入块结构。

GetHash() 字符串方法返回给定块的哈希值。

GetPrevHash() 字符串方法返回前一个块的哈希值。

SetPublicKey(pubk string) 方法将块创建者的公钥写入块。

因此,使用Block对象的方法,我们可以轻松地将其转换为可以通过网络传输的格式并保存到LevelDB数据库中。

区块链包的函数负责保存到区块链: github.com/Rusldv/bcstartup/tree/master/blockchain

为此,块必须实现 IBlock 接口:

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

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

}

当包在 init() 函数中初始化时,数据库连接就会创建一次:

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

CloseDB() 是 db.Cloce() 的包装器 - 在使用包函数关闭与数据库的连接后调用。

SetTargetBlockHash(hash string) 错误函数将具有由 BLOCK_HASH 常量指定的键的当前块的哈希值写入数据库。

GetTargetBlockHash()(字符串,错误)函数返回数据库中存储的当前块的哈希值。

SetTargetBlockHeight(height int) 错误函数将具有由 BLOCK_HEIGHT 常量指定的键的节点的区块链高度值写入数据库。

GetTargetBlockHeight() (int, error) 函数返回给定节点的区块链高度,存储在数据库中。

CheckBlock(block IBlock) bool 函数在将块添加到区块链之前检查该块的正确性。

AddBlock(block IBlock) 错误函数向区块链添加一个块。

检索和查看区块的函数位于区块链包的explore.go文件中:

GetBlockByHash(hash string) (*block.Block, error) 函数创建一个空块对象,从数据库加载一个块到其中,该块的哈希值被传递给它,并返回一个指向它的指针。

创世块的创建是通过区块链包的 genesis.go 文件中的 Genesis() 错误函数执行的。

下一篇文章将讨论使用 WebSocket 机制将客户端连接到节点。

来源: habr.com

添加评论