区块链实验五 : 挖矿交易2

您所在的位置:网站首页 区块链的基本结构和挖矿过程 区块链实验五 : 挖矿交易2

区块链实验五 : 挖矿交易2

2024-06-02 19:27| 来源: 网络整理| 查看: 265

实验报告05orz 实验目的及要求实验基本原理实验实施环境实验概述实验内容学习第五章 区块链挖矿交易实现区块链挖矿交易的代码编写项目目录截图原型代码Terminal运行结果截图 论述挖矿算法的使用意义

实验目的及要求

了解编程语言的基本逻辑,理解编程语言运行机理。熟悉区块链的原理。能正确使用编程语言搭建区块和区块链,并进行工作量证明实现。

实验基本原理

通过课堂理论教学准备,动手使用实验软件平台,完成慕课实验项目的要求。

实验实施环境

Goland & GoSDK

实验概述

编写区块链基本原型,加入挖矿交易详细算法。

实验内容 学习第五章 区块链挖矿交易

imooc学习地址↓ 5-3挖矿流程及代码实现 5-4交易记账流程及代码实现 学习总结: 区块链的交易机制如下图: 1 te代表每个交易,其中没被箭头连接到的output代表涉及到交易的账户余额。

实现区块链挖矿交易的代码编写 项目目录截图

1

原型代码 main.go /* * @Author: Chen Shan * @Date: 2020/10/19 09:48 */ package main import ( "core" ) func main() { cli := core.CLI{} cli.Run() } block.go /* * @Author: Chen Shan * @Date: 2020/10/19 09:48 */ package core import ( "bytes" "crypto/sha256" "encoding/gob" "log" "time" ) // 时间戳 // 交易 // 前一个块哈希 // 当前块哈希 // 难度值 type Block struct { Timestamp int64 Transactions []*Transaction PrevBlockHash []byte Hash []byte Nonce int } func (b *Block) Serialize() []byte { var result bytes.Buffer encoder := gob.NewEncoder(&result) err := encoder.Encode(b) if err != nil { log.Panic(err) } return result.Bytes() } // Deserialize a block func DeserializeBlock(d []byte) *Block { var block Block decoder := gob.NewDecoder(bytes.NewReader(d)) err := decoder.Decode(&block) if err != nil { log.Panic(err) } return &block } // NewBlock creates and returns Block func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block { block := &Block{ Timestamp: time.Now().Unix(), Transactions: transactions, PrevBlockHash: prevBlockHash, Hash: []byte{}, Nonce: 0} pow := NewProofOfWork(block) nonce, hash := pow.Run() block.Hash = hash[:] block.Nonce = nonce return block } // NewGenesisBlock creates and returns Genesis Block func NewGenesisBlock(coinbase *Transaction) *Block { return NewBlock([]*Transaction{coinbase}, []byte{}) } // 计算区块里所有交易的哈希 func (b *Block) HashTransactions() []byte { var txHashes [][]byte var txHash [32]byte for _, tx := range b.Transactions { txHashes = append(txHashes, tx.ID) } txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) return txHash[:] } blockchain.go /* * @Author: Chen Shan * @Date: 2020/10/19 09:49 */ package core import ( "encoding/hex" "fmt" "log" "os" "github.com/boltdb/bolt" ) const dbFile = "blockchain.db" const blocksBucket = "blocks" const genesisCoinbaseData = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks" type Blockchain struct { tip []byte db *bolt.DB } type BlockchainIterator struct { currentHash []byte db *bolt.DB } func (bc *Blockchain) MineBlock(transactions []*Transaction) { var lastHash []byte err := bc.db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) lastHash = b.Get([]byte("l")) return nil }) if err != nil { log.Panic(err) } newBlock := NewBlock(transactions, lastHash) err = bc.db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) err := b.Put(newBlock.Hash, newBlock.Serialize()) if err != nil { log.Panic(err) } err = b.Put([]byte("l"), newBlock.Hash) if err != nil { log.Panic(err) } bc.tip = newBlock.Hash return nil }) } func (bc *Blockchain) Iterator() *BlockchainIterator { bci := &BlockchainIterator{bc.tip, bc.db} return bci } func (i *BlockchainIterator) Next() *Block { var block *Block err := i.db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) encodedBlock := b.Get(i.currentHash) block = DeserializeBlock(encodedBlock) return nil }) if err != nil { log.Panic(err) } i.currentHash = block.PrevBlockHash return block } func dbExists() bool { if _, err := os.Stat(dbFile); os.IsNotExist(err) { return false } return true } // 创建一个有创世块的新链 func NewBlockchain(address string) *Blockchain { if dbExists() == false { fmt.Println("No existing blockchain found. Create one first.") os.Exit(1) } var tip []byte db, err := bolt.Open(dbFile, 0600, nil) if err != nil { log.Panic(err) } err = db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) tip = b.Get([]byte("l")) return nil }) if err != nil { log.Panic(err) } bc := Blockchain{tip, db} return &bc } // CreateBlockchain 创建一个新的区块链数据库 // address 用来接收挖出创世块的奖励 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) if err != nil { log.Panic(err) } err = db.Update(func(tx *bolt.Tx) error { cbtx := NewCoinbaseTX(address, genesisCoinbaseData) genesis := NewGenesisBlock(cbtx) b, err := tx.CreateBucket([]byte(blocksBucket)) if err != nil { log.Panic(err) } err = b.Put(genesis.Hash, genesis.Serialize()) if err != nil { log.Panic(err) } err = b.Put([]byte("l"), genesis.Hash) if err != nil { log.Panic(err) } tip = genesis.Hash return nil }) if err != nil { log.Panic(err) } bc := Blockchain{tip, db} return &bc } // FindUnspentTransactions 找到未花费输出的交易 func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { var unspentTXs []Transaction spentTXOs := make(map[string][]int) bci := bc.Iterator() for { block := bci.Next() for _, tx := range block.Transactions { txID := hex.EncodeToString(tx.ID) Outputs: for outIdx, out := range tx.Vout { // 如果交易输出被花费了 if spentTXOs[txID] != nil { for _, spentOut := range spentTXOs[txID] { if spentOut == outIdx { continue Outputs } } } // 如果该交易输出可以被解锁,即可被花费 if out.CanBeUnlockedWith(address) { unspentTXs = append(unspentTXs, *tx) } } if tx.IsCoinbase() == false { for _, in := range tx.Vin { if in.CanUnlockOutputWith(address) { inTxID := hex.EncodeToString(in.Txid) spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout) } } } } if len(block.PrevBlockHash) == 0 { break } } return unspentTXs } func (bc *Blockchain) FindUTXO(address string) []TXOutput { var UTXOs []TXOutput unspentTransactions := bc.FindUnspentTransactions(address) for _, tx := range unspentTransactions { for _, out := range tx.Vout { if out.CanBeUnlockedWith(address) { UTXOs = append(UTXOs, out) } } } return UTXOs } // FindSpendableOutputs 从 address 中找到至少 amount 的 UTXO func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { unspentOutputs := make(map[string][]int) unspentTXs := bc.FindUnspentTransactions(address) accumulated := 0 Work: for _, tx := range unspentTXs { txID := hex.EncodeToString(tx.ID) for outIdx, out := range tx.Vout { if out.CanBeUnlockedWith(address) && accumulated break Work } } } } return accumulated, unspentOutputs } cil.go /* * @Author: Chen Shan * @Date: 2020/10/19 09:49 */ package core import ( "flag" "fmt" "log" "os" "strconv" ) type CLI struct{} func (cli *CLI) createBlockchain(address string) { bc := CreateBlockchain(address) bc.db.Close() fmt.Println("Done!") } func (cli *CLI) getBalance(address string) { bc := NewBlockchain(address) defer bc.db.Close() balance := 0 UTXOs := bc.FindUTXO(address) for _, out := range UTXOs { balance += out.Value } fmt.Printf("Balance of '%s': %d\n", address, balance) } func (cli *CLI) printUsage() { fmt.Println("Usage:") fmt.Println(" getbalance -address ADDRESS - Get balance of ADDRESS") fmt.Println(" createblockchain -address ADDRESS - Create a blockchain and send genesis block reward to ADDRESS") fmt.Println(" printchain - Print all the blocks of the blockchain") fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send AMOUNT of coins from FROM address to TO") } func (cli *CLI) validateArgs() { if len(os.Args) bc := NewBlockchain("") defer bc.db.Close() bci := bc.Iterator() for { block := bci.Next() fmt.Printf("Prev hash: %x\n", block.PrevBlockHash) fmt.Printf("Hash: %x\n", block.Hash) pow := NewProofOfWork(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) fmt.Println() if len(block.PrevBlockHash) == 0 { break } } } func (cli *CLI) send(from, to string, amount int) { bc := NewBlockchain(from) defer bc.db.Close() tx := NewUTXOTransaction(from, to, amount, bc) bc.MineBlock([]*Transaction{tx}) fmt.Println("Success!") } func (cli *CLI) Run() { cli.validateArgs() getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) sendCmd := flag.NewFlagSet("send", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for") createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to") sendFrom := sendCmd.String("from", "", "Source wallet address") sendTo := sendCmd.String("to", "", "Destination wallet address") sendAmount := sendCmd.Int("amount", 0, "Amount to send") switch os.Args[1] { case "getbalance": err := getBalanceCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) } case "createblockchain": err := createBlockchainCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) } case "send": err := sendCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) } case "printchain": err := printChainCmd.Parse(os.Args[2:]) if err != nil { log.Panic(err) } default: cli.printUsage() os.Exit(1) } if getBalanceCmd.Parsed() { if *getBalanceAddress == "" { getBalanceCmd.Usage() os.Exit(1) } cli.getBalance(*getBalanceAddress) } if createBlockchainCmd.Parsed() { if *createBlockchainAddress == "" { createBlockchainCmd.Usage() os.Exit(1) } cli.createBlockchain(*createBlockchainAddress) } if printChainCmd.Parsed() { cli.printChain() } if sendCmd.Parsed() { if *sendFrom == "" || *sendTo == "" || *sendAmount block *Block target *big.Int } func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) target.Lsh(target, uint(256-targetBits)) pow := &ProofOfWork{b, target} return pow } 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 } func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0 fmt.Printf("Mining a new block") for nonce break } else { nonce++ } } fmt.Print("\n\n") return nonce, hash[:] } // Validate block's PoW func (pow *ProofOfWork) Validate() bool { var hashInt big.Int data := pow.prepareData(pow.block.Nonce) hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) isValid := hashInt.Cmp(pow.target) == -1 return isValid } transaction.go /* * @Author: Chen Shan * @Date: 2020/10/19 09:49 */ package core import ( "bytes" "crypto/sha256" "encoding/gob" "encoding/hex" "fmt" "log" ) const subsidy = 10 // Transaction 由交易 ID,输入和输出构成 type Transaction struct { ID []byte Vin []TXInput Vout []TXOutput } // TXInput 包含 3 部分 // Txid: 一个交易输入引用了之前一笔交易的一个输出, ID 表明是之前哪笔交易 // Vout: 一笔交易可能有多个输出,Vout 为输出的索引 // ScriptSig: 提供解锁输出 Txid:Vout 的数据 type TXInput struct { Txid []byte Vout int ScriptSig string } // TXOutput 包含两部分 // Value: 有多少币,就是存储在 Value 里面 // ScriptPubKey: 对输出进行锁定 // 在当前实现中,ScriptPubKey 将仅用一个字符串来代替 type TXOutput struct { Value int ScriptPubKey string } // IsCoinbase 判断是否是 coinbase 交易 func (tx Transaction) IsCoinbase() bool { return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1 } func (tx *Transaction) SetID() { var encoded bytes.Buffer var hash [32]byte enc := gob.NewEncoder(&encoded) err := enc.Encode(tx) if err != nil { log.Panic(err) } hash = sha256.Sum256(encoded.Bytes()) tx.ID = hash[:] } // 这里的 unlockingData 可以理解为地址 func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool { return in.ScriptSig == unlockingData } func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool { return out.ScriptPubKey == unlockingData } // NewCoinbaseTX 构建 coinbase 交易,该没有输入,只有一个输出 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() return &tx } // NewUTXOTransaction 创建一笔新的交易 func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction { var inputs []TXInput var outputs []TXOutput // 找到足够的未花费输出 acc, validOutputs := bc.FindSpendableOutputs(from, amount) if acc txID, err := hex.DecodeString(txid) if err != nil { log.Panic(err) } for _, out := range outs { input := TXInput{txID, out, from} inputs = append(inputs, input) } } outputs = append(outputs, TXOutput{amount, to}) // 如果 UTXO 总数超过所需,则产生找零 if acc > amount { outputs = append(outputs, TXOutput{acc - amount, from}) } tx := Transaction{nil, inputs, outputs} tx.SetID() return &tx } utils.go /* * @Author: Chen Shan * @Date: 2020/10/19 09:49 */ package core import ( "bytes" "encoding/binary" "log" ) // Convert an int64 to a byte array func IntToHex(num int64) []byte { buff := new(bytes.Buffer) err := binary.Write(buff, binary.BigEndian, num) if err != nil { log.Panic(err) } return buff.Bytes() } Terminal运行结果截图

1 2

论述挖矿算法的使用意义

就不告诉你~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3