Golang|区块链Transaction实现源码分析

区块链Transaction实现源码分析

资源

go实现区块链

程序运行结果

在这里插入图片描述

源码分析

当程序解析到命令行参数时,将调用结构体CLI的createBlockchain方法。这个方法的作用是在本地数据库中创建一个含有创世区块的区块链。(如果在本地数据库中已经含有区块链,则不执行创建)

在这里插入图片描述

进入到该方法的源码部分。

// CreateBlockchain creates a new blockchain DB
func CreateBlockchain(address string) *Blockchain {
	if dbExists() {
		fmt.Println("Blockchain already exists.")
		os.Exit(1)
	}

	var tip []byte
	db, err := bolt.Open(dbFile, 0600, nil) // 打开数据库
	// 打开一个数据库读写事务
	err = db.Update(func(tx *bolt.Tx) error {
		// 尝试获取存储区块的bucket
		b := tx.Bucket([]byte(blocksBucket))
			// 产生一个挖出创世区块的奖励事务
			cbtx := NewCoinbaseTX(address,genesisCoinbaseData)
			// 构造新区块来存储记录该事务
			genesis := NewGenesisBlock(cbtx)
			// 在数据库中创建一个bucket
			b, err := tx.CreateBucket([]byte(blocksBucket))
			// 将传世区块存储到bucket中
			err = b.Put(genesis.Hash, genesis.Serialize())
			// 更新bucket中存储的区块链最后一个区块的哈希值
			err = b.Put([]byte{1}, genesis.Hash)
			tip = genesis.Hash
			return err
		return nil
	})
	if err != nil {
		log.Panic(err.Error())
	}
	bc := Blockchain{tip, db}
	return &bc
}

观察上面的源码,其中比较关键的有两个方法。

一个方法是NewCoinbaseTX,另一个方法是NewGenesisBlock。在这段代码中,程序将产生一个挖出创世区块的Transaction,然后将Transaction存储在创世区块上面,最后将创世区块添加到区块链中(存到本地数据库)。

先进入NewCoinbaseTX方法,观察它做了什么操作。

func NewCoinbaseTX(to,data string)*Transaction {
	if data == "" {
		data = fmt.Sprintf("Reward to %s", to)
	}
	// 任意交易输入
	txin := TXInput{[]byte{}, -1, data}
	// 交易输出:奖励旷工虚拟货币
	txout := TXOutput{subsidy, to}
	// 构造交易
	tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}
	tx.SetID() // 设置交易ID
	return &tx
}

在NewCoinbaseTX中,它构造了一组输入交易、一组输出交易并且设置了Transaction的ID,完成Transaction的构造后,该方法将Transaction指针返回。

以下是SetID的源码。

func (tx *Transaction)SetID(){
	var result bytes.Buffer
	encoder := gob.NewEncoder(&result)
	encoder.Encode(tx)
	// 对tx的序列化结果进行sha256运算
	hash := sha256.Sum256(result.Bytes())
	// 将运算的32位结果作为tx的ID
	tx.ID = hash[:]
}

可以看到,该方法将Transaction进行了序列化,然后将序列化的结果进行了哈希运算,并且将哈希运算的值作为了Transaction的ID。这样做的目的是,我们可以通过Transaction的ID值来校验Transaction的内容(一组输入交易以及一组输出交易)是否发生了篡改。

NewCoinbaseTX方法执行完毕后,将进入到NewGenesisBlock方法中。

// 产生一个挖出创世区块的奖励事务
	cbtx := NewCoinbaseTX(address,genesisCoinbaseData)
// 构造新区块来存储记录该事务
	genesis := NewGenesisBlock(cbtx)

现在进入到NewGenesisBlock中,观察它做了什么操作。

func NewGenesisBlock(coinbase *Transaction) *Block {
	return NewBlock([]*Transaction{coinbase}, []byte{})
}

func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {
	block := Block{}
	block.Transactions = transactions
	block.Timestamp = time.Now().Unix()
	block.PrevBlockHash = prevBlockHash
	// 工作量证明
	pow := NewProofOfWork(&block)
	nonce, hash := pow.Run()
	block.Hash = hash[:]
	block.Nonce = nonce
	return &block
}

可以看到,在NewGenesisBlock方法中,调用NewBlock构造了一个Block,在NewBlock中,比较重要的是工作量证明部分。现在进入到pow.Run()方法中,观察这个方法做了什么操作。

func (pow *ProofOfWork) prepareData(nonce int) []byte {
	data := bytes.Join(
		[][]byte{
			pow.block.PrevBlockHash,
			pow.block.HashTransactions(),
			IntToHex(pow.block.Timestamp),
			IntToHex(int64(targetBits)),
			IntToHex(int64(nonce)),
		},
		[]byte{},
	)

	return data
}

// Run performs a proof-of-work
func (pow *ProofOfWork) Run() (int, []byte) {
	var hashInt big.Int
	var hash [32]byte
	nonce := 0

	for nonce < maxNonce {
		data := pow.prepareData(nonce)

		hash = sha256.Sum256(data)
		fmt.Printf("\r%x", hash)
		hashInt.SetBytes(hash[:])

		if hashInt.Cmp(pow.target) == -1 {
			break
		} else {
			nonce++
		}
	}
	fmt.Print("\n\n")

	return nonce, hash[:]
}

工作量证明部分,程序通过枚举nonce来寻找符合系统难度的一串哈希值。当工作量证明完成后(得到nonce与当前区块的hash值),也就相当于挖出了一个新的区块了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值