赞
踩
当程序解析到命令行参数时,将调用结构体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值),也就相当于挖出了一个新的区块了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。