├── .idea ├── modules.xml ├── publicChaorsChain.iml └── workspace.xml ├── README.md ├── part1-Basic-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ └── utils.go ├── goLang公链实战之区块链基础结构.md ├── main.go └── readMe ├── part10-address_Prototype ├── BLC │ ├── Base58.go │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── CLI.go │ ├── CLI_creatBlockchain.go │ ├── CLI_createWallet.go │ ├── CLI_getAddrestList.go │ ├── CLI_getBlance.go │ ├── CLI_printchain.go │ ├── CLI_send.go │ ├── ProofOfwork.go │ ├── TXInput.go │ ├── TXOutput.go │ ├── TXOutputs.go │ ├── Transaction.go │ ├── UTXO.go │ ├── UTXOSet.go │ ├── Utils.go │ ├── Wallet.go │ └── Wallets.go ├── Wallets.dat ├── chaorsBlockchain.db ├── goLang公链实战之钱包&地址.md ├── main ├── main.go └── readMe ├── part11-transaction_1_Prototype ├── BLC │ ├── Base58.go │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── CLI.go │ ├── CLI_creatBlockchain.go │ ├── CLI_createWallet.go │ ├── CLI_getAddrestList.go │ ├── CLI_getBlance.go │ ├── CLI_printchain.go │ ├── CLI_send.go │ ├── CLI_testMethod.go │ ├── ProofOfwork.go │ ├── TXInput.go │ ├── TXOutput.go │ ├── TXOutputs.go │ ├── Transaction.go │ ├── UTXO.go │ ├── UTXOSet.go │ ├── Utils.go │ ├── Wallet.go │ └── Wallets.go ├── Wallets.dat ├── chaorsBlockchain.db ├── goLang公链实战之交易(2).md ├── main ├── main.go └── readMe ├── part12-MerkleTree_Prototype ├── BLC │ ├── Base58.go │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── CLI.go │ ├── CLI_creatBlockchain.go │ ├── CLI_createWallet.go │ ├── CLI_getAddrestList.go │ ├── CLI_getBlance.go │ ├── CLI_printchain.go │ ├── CLI_send.go │ ├── CLI_testMethod.go │ ├── MerkleTree.go │ ├── ProofOfwork.go │ ├── TXInput.go │ ├── TXOutput.go │ ├── TXOutputs.go │ ├── Transaction.go │ ├── UTXO.go │ ├── UTXOSet.go │ ├── Utils.go │ ├── Wallet.go │ └── Wallets.go ├── Wallets.dat ├── chaorsBlockchain.db ├── goLang公链实战之MerkleTree.md ├── main ├── main.go └── readMe ├── part13-Network_Prototype ├── .idea │ ├── misc.xml │ ├── modules.xml │ ├── part13-Network_Prototype.iml │ ├── vcs.xml │ └── workspace.xml ├── BLC │ ├── Base58.go │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── CLI.go │ ├── CLI_StartNode.go │ ├── CLI_creatBlockchain.go │ ├── CLI_createWallet.go │ ├── CLI_getAddrestList.go │ ├── CLI_getBlance.go │ ├── CLI_printchain.go │ ├── CLI_send.go │ ├── CLI_testMethod.go │ ├── Constant.go │ ├── MerkleTree.go │ ├── ProofOfwork.go │ ├── Server.go │ ├── Server_BlockData.go │ ├── Server_GetBlocks.go │ ├── Server_GetData.go │ ├── Server_Handle.go │ ├── Server_Inv.go │ ├── Server_TxData.go │ ├── Server_Version.go │ ├── Server_send.go │ ├── Server_var.go │ ├── TXInput.go │ ├── TXOutput.go │ ├── TXOutputs.go │ ├── Transaction.go │ ├── UTXO.go │ ├── UTXOSet.go │ ├── Utils.go │ ├── Wallet.go │ └── Wallets.go ├── goLang公链实战之网络初窥.md ├── main.go └── readMe ├── part2-ProofOfWork-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── ProofOfwork.go │ └── utils.go ├── goLang公链实战之ProofOfWork.md ├── main.go └── readMe ├── part3-boltdb-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── ProofOfwork.go │ └── utils.go ├── chaorsBlock.db ├── goLang公链实战之boltdb数据库.md ├── main ├── main.go └── readMe ├── part4-DataPersistence-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── ProofOfwork.go │ └── utils.go ├── chaorsBlockchain.db ├── goLang公链实战之数据持久化.md ├── main ├── main.go └── readMe ├── part5-cli-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── ProofOfwork.go │ ├── cli.go │ └── utils.go ├── chaorsBlockchain.db ├── goLang公链实战之cli工具.md ├── main ├── main.go └── readMe ├── part6-cli1-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── ProofOfwork.go │ ├── cli.go │ └── utils.go ├── chaorsBlockchain.db ├── main ├── main.go └── readMe ├── part7-transaction-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── ProofOfwork.go │ ├── TXInput.go │ ├── TXOutput.go │ ├── Transaction.go │ ├── cli.go │ └── utils.go ├── chaorsBlockchain.db ├── goLang公链实战之交易(1).md ├── main ├── main.go └── readMe ├── part8-transfer-Prototype ├── BLC │ ├── Block.go │ ├── Blockchain.go │ ├── BlockchainIterator.go │ ├── ProofOfwork.go │ ├── TXInput.go │ ├── TXOutput.go │ ├── Transaction.go │ ├── cli.go │ └── utils.go ├── chaorsBlockchain.db ├── goLang公链实战之转账(1).md ├── main ├── main.go └── readMe └── part9-transfer_1-Prototype ├── BLC ├── Block.go ├── Blockchain.go ├── BlockchainIterator.go ├── CLI.go ├── CLI_creatBlockchain.go ├── CLI_getBlance.go ├── CLI_printchain.go ├── CLI_send.go ├── ProofOfwork.go ├── TXInput.go ├── TXOutput.go ├── Transaction.go ├── UTXO.go └── utils.go ├── chaorsBlockchain.db ├── goLang公链实战之转账(2).md ├── main ├── main.go └── readMe /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/publicChaorsChain.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PublicBlockChain_go 2 | # 基于goLang的公链实现(具备公链全功能) 3 | 4 | # 前言 5 | 6 | 之前在github上看到一位歪果友人Ivan Kuznetsov用go实现的公链Demo,正好自己也在学习区块链相关知识,于是想记录自己是如何一步步实现基于GoLang的公链项目。喜欢的朋友可以给个star权当鼓励,也可以一起探讨区块链相关知识,我们共同进步。 7 | 8 | # 目录 9 | 10 | ### 1.[区块链基础结构(part1)](https://github.com/chaors/PublicBlockChain_go/blob/master/part1-Basic-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E5%8C%BA%E5%9D%97%E9%93%BE%E5%9F%BA%E7%A1%80%E7%BB%93%E6%9E%84.md) 11 | 12 | ### 2.[ProofOfWork(part2)](https://github.com/chaors/PublicBlockChain_go/blob/master/part2-ProofOfWork-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8BProofOfWork.md) 13 | 14 | ### 3.[BoltDB(part3)](https://github.com/chaors/PublicBlockChain_go/blob/master/part3-boltdb-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8Bboltdb%E6%95%B0%E6%8D%AE%E5%BA%93.md) 15 | 16 | ### 4.[链上数据持久化(part4)](https://github.com/chaors/PublicBlockChain_go/blob/master/part4-DataPersistence-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96.md) 17 | 18 | ### 5.[CLI命令行工具(part5/part6)](https://github.com/chaors/PublicBlockChain_go/blob/master/part5-cli-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8Bcli%E5%B7%A5%E5%85%B7.md) 19 | 20 | ### 6.[交易TransAction1(part7)](https://github.com/chaors/PublicBlockChain_go/blob/master/part7-transaction-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E4%BA%A4%E6%98%93(1).md) 21 | 22 | ### 7.[转账1(part8)](https://github.com/chaors/PublicBlockChain_go/blob/master/part8-transfer-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E8%BD%AC%E8%B4%A6(1).md) 23 | 24 | ### 8.[转账2(part9)](https://github.com/chaors/PublicBlockChain_go/blob/master/part9-transfer_1-Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E8%BD%AC%E8%B4%A6(2).md) 25 | 26 | ### 9.[钱包地址Address(part10)](https://github.com/chaors/PublicBlockChain_go/blob/master/part10-address_Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E9%92%B1%E5%8C%85%26%E5%9C%B0%E5%9D%80.md) 27 | 28 | ### 10.[交易TransAction2(part11)](https://github.com/chaors/PublicBlockChain_go/blob/master/part11-transaction_1_Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E4%BA%A4%E6%98%93(2).md) 29 | 30 | ### 11.[MerkleTree(part12)](https://github.com/chaors/PublicBlockChain_go/blob/master/part12-MerkleTree_Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8BMerkleTree.md) 31 | 32 | ### 12.[网络初窥(多节点区块同步)(part13)](https://github.com/chaors/PublicBlockChain_go/blob/master/part13-Network_Prototype/goLang%E5%85%AC%E9%93%BE%E5%AE%9E%E6%88%98%E4%B9%8B%E7%BD%91%E7%BB%9C%E5%88%9D%E7%AA%A5.md) 33 | 34 | ### 13.还在陆续更新中.... 35 | 36 | ### ...... 37 | 38 | 39 | 40 | # 更多区块链技术文章请访问[chaors](https://www.jianshu.com/c/6277257ba30a) 41 | 42 | 43 | # 参考资料 44 | 45 | ### 1. [Building Blockchain in Go-Ivan Kuznetso](https://jeiwan.cc/tags/blockchain/) 46 | 47 | ### 2.[用 golang 从零开始构建区块链(Bitcoin)系列](https://liuchengxu.gitbooks.io/blockchain-tutorial/content/) 48 | 49 | ### 3.[《精通比特币第二版》](http://book.8btc.com/books/6/masterbitcoin2cn/_book/trans-preface.html) 50 | 51 | ### 4.[Bitcoin Developer Documentation](https://bitcoin.org/en/developer-documentation) 52 | 53 | ### 5.[bitcoin wiki](https://en.bitcoin.it/wiki/Main_Page) 54 | 55 | -------------------------------------------------------------------------------- /part1-Basic-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "strconv" 16 | "fmt" 17 | "bytes" 18 | "crypto/sha256" 19 | ) 20 | 21 | type Block struct { 22 | //1.区块高度 23 | Height int64 24 | //2.上一个区块HAsh 25 | PrevBlockHash []byte 26 | //3.交易数据 27 | Data []byte 28 | //4.时间戳 29 | Timestamp int64 30 | //5.Hash 31 | Hash []byte 32 | } 33 | 34 | //设置当前区块Hash 35 | func (block *Block) SetHash() { 36 | 37 | //1.将高度,时间戳转换为字节数组 38 | //base:2 二进制形式 39 | heightBytes := IntToHex(block.Height) 40 | 41 | timeStampStr := strconv.FormatInt(block.Timestamp, 2) 42 | timeStamp := []byte(timeStampStr) 43 | 44 | //fmt.Println(heightBytes) 45 | //fmt.Println(timeStampStr) 46 | //fmt.Println(timeStamp) 47 | 48 | //2.拼接所有属性 49 | blockBytes := bytes.Join([][]byte{ 50 | heightBytes, 51 | block.PrevBlockHash, 52 | block.Data, 53 | timeStamp, 54 | block.Hash}, []byte{}) 55 | //fmt.Println(blockBytes) 56 | 57 | //3.将拼接后的字节数组转换为Hash值 58 | hash := sha256.Sum256(blockBytes) 59 | fmt.Println(hash) 60 | 61 | block.Hash = hash[:] 62 | //fmt.Println(block.Hash) 63 | 64 | } 65 | 66 | //1.创建新的区块 67 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 68 | 69 | //创建区块 70 | block := &Block{ 71 | Height: height, 72 | PrevBlockHash: prevBlockHash, 73 | Data: []byte(data), 74 | Timestamp: time.Now().Unix(), 75 | Hash: nil} 76 | 77 | //设置HAsh值 78 | block.SetHash() 79 | 80 | return block 81 | } 82 | 83 | //单独方法生成创世区块 84 | func CreateGenesisBlock(data string) *Block { 85 | 86 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 87 | } 88 | 89 | -------------------------------------------------------------------------------- /part1-Basic-Prototype/BLC/Blockchain.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Blockchain.go 5 | 6 | @time: 2018/06/21 22:40 7 | 8 | @desc: 区块链基础结构 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | type Blockchain struct { 15 | //有序区块的数组 16 | Blocks [] *Block 17 | } 18 | 19 | //1.创建带有创世区块的区块链 20 | func CreateBlockchainWithGensisBlock() *Blockchain { 21 | 22 | gensisBlock := CreateGenesisBlock("Gensis Block...") 23 | 24 | return &Blockchain{[] *Block{gensisBlock}} 25 | } 26 | 27 | //2.新增一个区块到区块链 28 | func (blc *Blockchain) AddBlockToBlockchain(data string, height int64, prevHash []byte) { 29 | 30 | //新建区块 31 | newBlock := NewBlock(data, height, prevHash) 32 | //上链 33 | blc.Blocks = append(blc.Blocks, newBlock) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /part1-Basic-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part1-Basic-Prototype/goLang公链实战之区块链基础结构.md: -------------------------------------------------------------------------------- 1 | # goLang公链实战之区块链基础结构 2 | 3 | 堕落了一段时间,终于又找回了学习的动力,满血归来。。。 4 | 5 | 我们知道在如火如荼的区块链应用红海,goLang越来越多地发挥着不可替代的作用。一方面取决于其语法的简单性,一方面其具备C++高效处理的特性。今天,我们就用go语言开始构建一个简单但是具备区块链完整功能的公链项目。 6 | 7 | 由于之前已经用[Python构建过简单的区块链结构](https://www.jianshu.com/p/ecfb2a9040a3),所以对区块基本结构的东西不再做详细赘述。 8 | 9 | # 废话少说上干货 10 | 11 | ## 区块Block 12 | 13 | #### Block 14 | ``` 15 | type Block struct { 16 | //1.区块高度 17 | Height int64 18 | //2.上一个区块HAsh 19 | PrevBlockHash []byte 20 | //3.交易数据 21 | Data []byte 22 | //4.时间戳 23 | Timestamp int64 24 | //5.Hash 25 | Hash []byte 26 | } 27 | ``` 28 | 29 | #### 设置当前区块Hash 30 | ``` 31 | func (block *Block) SetHash() { 32 | 33 | //1.将高度,时间戳转换为字节数组 34 | //base:2 二进制形式 35 | heightBytes := IntToHex(block.Height) 36 | 37 | timeStampStr := strconv.FormatInt(block.Timestamp, 2) 38 | timeStamp := []byte(timeStampStr) 39 | 40 | //fmt.Println(heightBytes) 41 | //fmt.Println(timeStampStr) 42 | //fmt.Println(timeStamp) 43 | 44 | //2.拼接所有属性 45 | blockBytes := bytes.Join([][]byte{ 46 | heightBytes, 47 | block.PrevBlockHash, 48 | block.Data, 49 | timeStamp, 50 | block.Hash}, []byte{}) 51 | //fmt.Println(blockBytes) 52 | 53 | //3.将拼接后的字节数组转换为Hash值 54 | hash := sha256.Sum256(blockBytes) 55 | fmt.Println(hash) 56 | 57 | block.Hash = hash[:] 58 | //fmt.Println(block.Hash) 59 | 60 | } 61 | ``` 62 | 63 | #### 创建新区快 64 | ``` 65 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 66 | 67 | //创建区块 68 | block := &Block{ 69 | Height: height, 70 | PrevBlockHash: prevBlockHash, 71 | Data: []byte(data), 72 | Timestamp: time.Now().Unix(), 73 | Hash: nil} 74 | 75 | //设置HAsh值 76 | block.SetHash() 77 | 78 | return block 79 | } 80 | ``` 81 | 82 | #### 创世区块创建 83 | ``` 84 | func CreateGenesisBlock(data string) *Block { 85 | 86 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 87 | } 88 | ``` 89 | 90 | ### 区块链BlockChain 91 | 92 | #### 区块链 93 | ``` 94 | type Blockchain struct { 95 | //有序区块的数组 96 | Blocks [] *Block 97 | } 98 | ``` 99 | 100 | #### 创建带有创世区块的区块链 101 | ``` 102 | func CreateBlockchainWithGensisBlock() *Blockchain { 103 | 104 | gensisBlock := CreateGenesisBlock("Gensis Block...") 105 | 106 | return &Blockchain{[] *Block{gensisBlock}} 107 | } 108 | ``` 109 | 110 | #### 新增一个区块到区块链 111 | ``` 112 | func (blc *Blockchain) AddBlockToBlockchain(data string, height int64, prevHash []byte) { 113 | 114 | //新建区块 115 | newBlock := NewBlock(data, height, prevHash) 116 | //上链 117 | blc.Blocks = append(blc.Blocks, newBlock) 118 | } 119 | ``` 120 | 121 | ### utils辅助工具(int64转化为byte数组) 122 | ``` 123 | 124 | import ( 125 | "bytes" 126 | "encoding/binary" 127 | "log" 128 | ) 129 | 130 | //将int64转换为bytes 131 | func IntToHex(num int64) []byte { 132 | 133 | buff := new(bytes.Buffer) 134 | err := binary.Write(buff, binary.BigEndian, num) 135 | if err != nil { 136 | 137 | log.Panic(err) 138 | } 139 | 140 | return buff.Bytes() 141 | } 142 | ``` 143 | 144 | ### 测试Demo 145 | ``` 146 | /** 147 | @author: chaors 148 | 149 | @file: main.go 150 | 151 | @time: 2018/06/21 22:01 152 | 153 | @desc: 区块信息的示例 154 | */ 155 | 156 | package main 157 | 158 | import ( 159 | "chaors.com/LearnGo/publicChaorsChain/part1-Basic-Prototype/BLC" 160 | "fmt" 161 | ) 162 | 163 | func main() { 164 | 165 | //genesisBlock := BLC.CreateGenesisBlock("Genenis Block") 166 | //创建带有创世区块的区块链 167 | blockchain := BLC.CreateBlockchainWithGensisBlock() 168 | //添加一个新区快 169 | blockchain.AddBlockToBlockchain("first Block", 170 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 171 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 172 | blockchain.AddBlockToBlockchain("second Block", 173 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 174 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 175 | blockchain.AddBlockToBlockchain("third Block", 176 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 177 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 178 | fmt.Println(blockchain) 179 | } 180 | ``` 181 | 182 | ### 运行结果 183 | 184 | 我们看到运行的结果,打印的内容为包含创世区块在内的四个区块的区块链。 185 | 186 | ![image.png](https://upload-images.jianshu.io/upload_images/830585-9a952234ba83dd97.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /part1-Basic-Prototype/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: main.go 5 | 6 | @time: 2018/06/21 22:01 7 | 8 | @desc: 区块信息的示例 9 | */ 10 | 11 | 12 | package main 13 | 14 | import ( 15 | "chaors.com/LearnGo/publicChaorsChain/part1-Basic-Prototype/BLC" 16 | "fmt" 17 | ) 18 | 19 | func main() { 20 | 21 | //genesisBlock := BLC.CreateGenesisBlock("Genenis Block") 22 | //创建带有创世区块的区块链 23 | blockchain := BLC.CreateBlockchainWithGensisBlock() 24 | //添加一个新区快 25 | blockchain.AddBlockToBlockchain("first Block", 26 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 27 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 28 | blockchain.AddBlockToBlockchain("second Block", 29 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 30 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 31 | blockchain.AddBlockToBlockchain("third Block", 32 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 33 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 34 | fmt.Println(blockchain) 35 | } 36 | -------------------------------------------------------------------------------- /part1-Basic-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 区块链基本结构 3 | **/ -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/Base58.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | ) 7 | 8 | /** 9 | Base58是用于比特币中使用的一种独特的编码方式,主要用于产生比特币的钱包地址。 10 | 11 | 相比的Base64,Base58不使用数字 “0”,字母大写 “O”,字母大写 “I”,和字母小写 “L”,以及 “+” 和 “/” 符号。 12 | 13 | 设计Base58主要的目的是: 14 | 15 | 避免混淆。在某些字体下,数字0和字母大写O,以及字母大写我和字母小写升会非常相似。 16 | 不使用 “+” 和 “/” 的原因是非字母或数字的字符串作为帐号较难被接受。 17 | 没有标点符号,通常不会被从中间分行。 18 | 大部分的软件支持双击选择整个字符串。 19 | 但是这个base58的计算量比BASE64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。 20 | 而且长度也比的base64稍微多了一点。 21 | */ 22 | 23 | //base64:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 24 | //base58:去掉0(零),O(大写的 o),I(大写的i),l(小写的 L),+,/ 25 | 26 | //base58编码集 27 | var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 28 | 29 | // 字节数组转 Base58,加密 30 | func Base58Encode(input []byte) []byte { 31 | 32 | var result []byte 33 | 34 | x := big.NewInt(0).SetBytes(input) 35 | 36 | base := big.NewInt(int64(len(b58Alphabet))) 37 | zero := big.NewInt(0) 38 | mod := &big.Int{} 39 | 40 | for x.Cmp(zero) != 0 { 41 | 42 | x.DivMod(x, base, mod) 43 | result = append(result, b58Alphabet[mod.Int64()]) 44 | } 45 | 46 | ReverseBytes(result) 47 | for b := range input { 48 | 49 | if b == 0x00 { 50 | 51 | result = append([]byte{b58Alphabet[0]}, result...) 52 | } else { 53 | 54 | break 55 | } 56 | } 57 | 58 | return result 59 | } 60 | 61 | // Base58转字节数组,解密 62 | func Base58Decode(input []byte) []byte { 63 | 64 | result := big.NewInt(0) 65 | zeroBytes := 0 66 | 67 | for b := range input { 68 | 69 | if b == 0x00 { 70 | 71 | zeroBytes++ 72 | } 73 | } 74 | 75 | payload := input[zeroBytes:] 76 | for _, b := range payload { 77 | 78 | charIndex := bytes.IndexByte(b58Alphabet, b) 79 | result.Mul(result, big.NewInt(58)) 80 | result.Add(result, big.NewInt(int64(charIndex))) 81 | } 82 | 83 | decoded := result.Bytes() 84 | //decoded...表示将decoded所有字节追加 85 | decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...) 86 | 87 | return decoded 88 | } 89 | 90 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | "crypto/sha256" 20 | ) 21 | 22 | type Block struct { 23 | //1.区块高度 24 | Height int64 25 | //2.上一个区块HAsh 26 | PrevBlockHash []byte 27 | //3.交易数据 28 | Txs [] *Transaction 29 | //4.时间戳 30 | Timestamp int64 31 | //5.Hash 32 | Hash []byte 33 | //6.Nonce 符合工作量证明的随机数 34 | Nonce int64 35 | } 36 | 37 | //区块序列化 38 | func (block *Block) Serialize() []byte { 39 | 40 | var result bytes.Buffer 41 | 42 | encoder := gob.NewEncoder(&result) 43 | 44 | err := encoder.Encode(block) 45 | if err != nil { 46 | log.Panic(err) 47 | } 48 | 49 | return result.Bytes() 50 | } 51 | 52 | //区块反序列化 53 | func DeSerializeBlock(blockBytes []byte) *Block { 54 | 55 | var block Block 56 | 57 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 58 | 59 | err := decoder.Decode(&block) 60 | if err != nil { 61 | log.Panic(err) 62 | } 63 | 64 | return &block 65 | } 66 | 67 | 68 | //1.创建新的区块 69 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 70 | 71 | //创建区块 72 | block := &Block{ 73 | Height: height, 74 | PrevBlockHash: prevBlockHash, 75 | Txs: txs, 76 | Timestamp: time.Now().Unix(), 77 | Hash: nil, 78 | Nonce: 0} 79 | 80 | //调用工作量证明返回有效的Hash 81 | pow := NewProofOfWork(block) 82 | hash, nonce := pow.Run() 83 | block.Hash = hash[:] 84 | block.Nonce = nonce 85 | 86 | fmt.Printf("\r######%d-%x\n", nonce, hash) 87 | 88 | return block 89 | } 90 | 91 | //单独方法生成创世区块 92 | func CreateGenesisBlock(txs []*Transaction) *Block { 93 | 94 | return NewBlock( 95 | txs, 96 | 1, 97 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 98 | ) 99 | } 100 | 101 | // 需要将Txs转换成[]byte 102 | func (block *Block) HashTransactions() []byte { 103 | 104 | var txHashes [][]byte 105 | var txHash [32]byte 106 | 107 | for _, tx := range block.Txs { 108 | 109 | txHashes = append(txHashes, tx.TxHAsh) 110 | } 111 | txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 112 | 113 | return txHash[:] 114 | 115 | } 116 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error{ 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | 24 | if b != nil { 25 | currentBloclBytes := b.Get(blcIterator.CurrHash) 26 | 27 | // 获取到当前迭代器里面的currentHash所对应的区块 28 | block = DeSerializeBlock(currentBloclBytes) 29 | 30 | // 更新迭代器里面CurrentHash 31 | blcIterator.CurrHash = block.PrevBlockHash 32 | } 33 | 34 | return nil 35 | }) 36 | if err != nil { 37 | log.Panic(err) 38 | } 39 | 40 | return block 41 | } 42 | 43 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | "github.com/blockchain_go_videos-master/part53-wallets/BLC" 9 | ) 10 | 11 | type CLI struct { 12 | 13 | } 14 | 15 | //打印目前左右命令使用方法 16 | func printUsage() { 17 | fmt.Println("Usage:") 18 | fmt.Println("\tcreateBlockchain -address --创世区块地址 ") 19 | fmt.Println("\tsend -from FROM -to TO -amount AMOUNT --交易明细") 20 | fmt.Println("\tprintchain --打印所有区块信息") 21 | fmt.Println("\tgetbalance -address -- 输出区块信息.") 22 | fmt.Println("\tcreateWallet -- 创建钱包.") 23 | fmt.Println("\tgetAddressList -- 输出所有钱包地址.") 24 | } 25 | 26 | func isValidArgs() { 27 | 28 | //获取当前输入参数个数 29 | if len(os.Args) < 2 { 30 | printUsage() 31 | os.Exit(1) 32 | } 33 | } 34 | 35 | func (cli *CLI) Run() { 36 | 37 | isValidArgs() 38 | 39 | //自定义cli命令 40 | sendBlockCmd := flag.NewFlagSet("send", flag.ExitOnError) 41 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 42 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 43 | blanceBlockCmd := flag.NewFlagSet("getBalance", flag.ExitOnError) 44 | createWalletCmd := flag.NewFlagSet("createWallet", flag.ExitOnError) 45 | getAddressListCmd := flag.NewFlagSet("getAddressList", flag.ExitOnError) 46 | 47 | //addBlockCmd 设置默认参数 48 | flagSendBlockFrom := sendBlockCmd.String("from", "", "源地址") 49 | flagSendBlockTo := sendBlockCmd.String("to", "", "目标地址") 50 | flagSendBlockAmount := sendBlockCmd.String("amount", "", "转账金额") 51 | flagCreateBlockchainAddress := createBlockchainCmd.String("address", "", "创世区块地址") 52 | flagBlanceBlockAddress := blanceBlockCmd.String("address", "", "输出区块信息") 53 | 54 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 55 | switch os.Args[1] { 56 | case "send": 57 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 58 | err := sendBlockCmd.Parse(os.Args[2:]) 59 | if err != nil { 60 | log.Panic(err) 61 | } 62 | case "printchain": 63 | err := printchainCmd.Parse(os.Args[2:]) 64 | if err != nil { 65 | log.Panic(err) 66 | } 67 | case "createBlockchain": 68 | err := createBlockchainCmd.Parse(os.Args[2:]) 69 | if err != nil { 70 | log.Panic(err) 71 | } 72 | case "getBalance": 73 | err := blanceBlockCmd.Parse(os.Args[2:]) 74 | if err != nil { 75 | log.Panic(err) 76 | } 77 | case "createWallet": 78 | err := createWalletCmd.Parse(os.Args[2:]) 79 | if err != nil { 80 | log.Panic(err) 81 | } 82 | case "getAddressList": 83 | err := getAddressListCmd.Parse(os.Args[2:]) 84 | if err != nil { 85 | log.Panic(err) 86 | } 87 | default: 88 | printUsage() 89 | os.Exit(1) 90 | } 91 | 92 | //对addBlockCmd命令的解析 93 | if sendBlockCmd.Parsed() { 94 | 95 | if *flagSendBlockFrom == "" { 96 | 97 | printUsage() 98 | os.Exit(1) 99 | } 100 | if *flagSendBlockTo == "" { 101 | 102 | printUsage() 103 | os.Exit(1) 104 | } 105 | if *flagSendBlockAmount == "" { 106 | 107 | printUsage() 108 | os.Exit(1) 109 | } 110 | 111 | //cli.addBlock(*flagAddBlockData) 112 | 113 | //这里真正地调用转账方法 114 | from := Json2Array(*flagSendBlockFrom) 115 | to := Json2Array(*flagSendBlockTo) 116 | 117 | //输入地址有效性判断 118 | for index, fromAddress := range from { 119 | 120 | if BLC.IsValidForAdress([]byte(fromAddress)) == false || BLC.IsValidForAdress([]byte(to[index])) == false { 121 | 122 | fmt.Printf("地址%s无效", fromAddress) 123 | os.Exit(1) 124 | } 125 | } 126 | 127 | amount := Json2Array(*flagSendBlockAmount) 128 | 129 | cli.send(from, to, amount) 130 | } 131 | //对printchainCmd命令的解析 132 | if printchainCmd.Parsed() { 133 | 134 | cli.printchain() 135 | } 136 | //创建区块链 137 | if createBlockchainCmd.Parsed() { 138 | 139 | if *flagCreateBlockchainAddress == "" { 140 | 141 | cli.creatBlockchain(*flagCreateBlockchainAddress) 142 | } 143 | 144 | cli.creatBlockchain(*flagCreateBlockchainAddress) 145 | } 146 | 147 | //查询余额 148 | if blanceBlockCmd.Parsed() { 149 | 150 | if *flagBlanceBlockAddress == "" { 151 | 152 | printUsage() 153 | os.Exit(1) 154 | } 155 | 156 | cli.getBlance(*flagBlanceBlockAddress) 157 | } 158 | 159 | //创建钱包 160 | if createWalletCmd.Parsed() { 161 | 162 | cli.createWallet() 163 | } 164 | 165 | //获取所有钱包地址 166 | if getAddressListCmd.Parsed() { 167 | 168 | cli.getAddressList() 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI_creatBlockchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //新建区块链 6 | func (cli *CLI)creatBlockchain(address string) { 7 | 8 | blockchain := CreateBlockchainWithGensisBlock(address) 9 | defer blockchain.DB.Close() 10 | } 11 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI_createWallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI)createWallet() { 6 | 7 | wallets, _ := NewWallets() 8 | wallets.CreateWallet() 9 | 10 | fmt.Println(len(wallets.Wallets)) 11 | } 12 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI_getAddrestList.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) getAddressList() { 6 | 7 | fmt.Println("All addresses:") 8 | 9 | wallets, _ := NewWallets() 10 | for address, _ := range wallets.Wallets { 11 | 12 | fmt.Println(address) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI_getBlance.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | 6 | //查询余额 7 | func (cli *CLI) getBlance(address string) { 8 | 9 | fmt.Println("地址:" + address) 10 | 11 | blockchain := GetBlockchain() 12 | defer blockchain.DB.Close() 13 | 14 | amount := blockchain.GetBalance(address) 15 | 16 | fmt.Printf("%s一共有%d个Token\n", address, amount) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI_printchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | //打印区块链 5 | func (cli *CLI) printchain() { 6 | 7 | blockchain := GetBlockchain() 8 | defer blockchain.DB.Close() 9 | 10 | blockchain.Printchain() 11 | } 12 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/CLI_send.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //转账 6 | func (cli *CLI) send(from []string, to []string, amount []string) { 7 | 8 | blockchain := GetBlockchain() 9 | defer blockchain.DB.Close() 10 | 11 | blockchain.MineNewBlock(from, to, amount) 12 | } -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "bytes" 4 | 5 | type TXInput struct { 6 | //交易ID 7 | TxHash []byte 8 | //存储TXOutput在Vouts里的索引 9 | Vout int 10 | //数字签名 11 | Signature []byte 12 | //公钥 13 | PublicKey []byte 14 | } 15 | 16 | //验证当前输入是否是当前地址的 17 | func (txInput *TXInput) UnlockWithAddress(address string) bool { 18 | 19 | //base58解码 20 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 21 | //去除版本得到地反编码的公钥两次哈希后的值 22 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 23 | 24 | //Ripemd160Hash算法得到公钥两次哈希后的值 25 | ripemd160HashNew := Ripemd160Hash(txInput.PublicKey) 26 | 27 | return bytes.Compare(ripemd160HashNew, ripemd160Hash) == 0 28 | } -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | type TXOutput struct { 9 | //面值 10 | Value int64 11 | //用户名 12 | Ripemd160Hash []byte //用户名 公钥两次哈希后的值 13 | } 14 | 15 | func NewTXOutput(value int64,address string) *TXOutput { 16 | 17 | txOutput := &TXOutput{value,nil} 18 | 19 | // 设置Ripemd160Hash 20 | txOutput.Lock(address) 21 | 22 | return txOutput 23 | } 24 | 25 | //锁定 26 | func (txOutput *TXOutput) Lock(address string) { 27 | 28 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 29 | txOutput.Ripemd160Hash = version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 30 | } 31 | 32 | //解锁 33 | func (txOutput *TXOutput) UnLockScriptPubKeyWithAddress(address string) bool { 34 | 35 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 36 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes) - 4] 37 | 38 | //fmt.Println(txOutput.Ripemd160Hash, ripemd160Hash) 39 | return bytes.Compare(txOutput.Ripemd160Hash, ripemd160Hash) == 0 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/TXOutputs.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "log" 6 | "bytes" 7 | ) 8 | 9 | type TXOutputs struct { 10 | 11 | TxOutputs []*TXOutput 12 | } 13 | 14 | 15 | // 序列化成字节数组 16 | func (txOutputs *TXOutputs) Serialize() []byte { 17 | 18 | var result bytes.Buffer 19 | 20 | encoder := gob.NewEncoder(&result) 21 | 22 | err := encoder.Encode(txOutputs) 23 | if err != nil { 24 | log.Panic(err) 25 | } 26 | 27 | return result.Bytes() 28 | } 29 | 30 | // 反序列化 31 | func DeserializeTXOutputs(txOutputsBytes []byte) *TXOutputs { 32 | 33 | var txOutputs TXOutputs 34 | 35 | decoder := gob.NewDecoder(bytes.NewReader(txOutputsBytes)) 36 | err := decoder.Decode(&txOutputs) 37 | if err != nil { 38 | 39 | log.Panic(err) 40 | } 41 | 42 | return &txOutputs 43 | } -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/UTXO.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type UTXO struct { 4 | //来自交易的哈希 5 | TxHash []byte 6 | //在该交易VOuts里的下标 7 | Index int 8 | //未花费的交易输出 9 | Output *TXOutput 10 | } 11 | 12 | -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/UTXOSet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | "encoding/hex" 7 | ) 8 | 9 | //存储UTXO的表 10 | const UTXOTableName = "UTXOTableName" 11 | 12 | type UTXOSet struct { 13 | 14 | Blc *Blockchain 15 | } 16 | 17 | //重置数据库表 18 | func (utxoSet *UTXOSet) ResetUTXOSet() { 19 | 20 | err := utxoSet.Blc.DB.Update(func(tx *bolt.Tx) error { 21 | 22 | //删除已经存在的UTXO表 23 | b := tx.Bucket([]byte(UTXOTableName)) 24 | if b != nil{ 25 | 26 | tx.DeleteBucket([]byte(UTXOTableName)) 27 | 28 | } 29 | 30 | //新建UTXO表 31 | b, _ = tx.CreateBucket([]byte(UTXOTableName)) 32 | if b != nil{ 33 | 34 | //找到区块链所有的UTXO 35 | txOutputMap := utxoSet.Blc.FindUTXOMAp() 36 | for keyHAsh , outs := range txOutputMap { 37 | 38 | txHash, _ := hex.DecodeString(keyHAsh) 39 | b.Put(txHash, outs.Se()) 40 | } 41 | 42 | } 43 | 44 | return nil 45 | }) 46 | if err != nil { 47 | 48 | log.Panic(err) 49 | } 50 | 51 | 52 | } -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/Utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | "encoding/json" 19 | ) 20 | 21 | //将int64转换为bytes 22 | func IntToHex(num int64) []byte { 23 | 24 | buff := new(bytes.Buffer) 25 | err := binary.Write(buff, binary.BigEndian, num) 26 | if err != nil { 27 | 28 | log.Panic(err) 29 | } 30 | 31 | return buff.Bytes() 32 | } 33 | 34 | // 标准的JSON字符串转数组 35 | func Json2Array(jsonString string) []string { 36 | 37 | //json 到 []string 38 | var sArr []string 39 | if err := json.Unmarshal([]byte(jsonString), &sArr); err != nil { 40 | 41 | log.Panic(err) 42 | } 43 | return sArr 44 | } 45 | 46 | 47 | // 字节数组反转 48 | func ReverseBytes(data []byte) { 49 | 50 | for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 { 51 | 52 | data[i], data[j] = data[j], data[i] 53 | } 54 | } -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/Wallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "log" 8 | "crypto/sha256" 9 | "golang.org/x/crypto/ripemd160" 10 | "bytes" 11 | ) 12 | 13 | //用于生成地址的版本 14 | const Version = byte(0x00) 15 | //用于生成地址的校验和位数 16 | const AddressChecksumLen = 4 17 | 18 | 19 | type Wallet struct { 20 | //私钥 21 | PrivateKey ecdsa.PrivateKey 22 | //公钥 23 | PublicKey []byte 24 | } 25 | 26 | //1.创建钱包 27 | func NewWallet () *Wallet { 28 | 29 | privateKey, publicKey := newKeyPair() 30 | 31 | //fmt.Println(privateKey, "\n\n", publicKey) 32 | 33 | return &Wallet{privateKey, publicKey} 34 | } 35 | 36 | //通过私钥创建公钥 37 | func newKeyPair() (ecdsa.PrivateKey, []byte) { 38 | 39 | //1.椭圆曲线算法生成私钥 40 | curve := elliptic.P256() 41 | privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) 42 | if err != nil { 43 | 44 | log.Panic(err) 45 | } 46 | 47 | //2.通过私钥生成公钥 48 | publicKey := append(privateKey.PublicKey.X.Bytes(), privateKey.PublicKey.Y.Bytes()...) 49 | 50 | 51 | return *privateKey, publicKey 52 | } 53 | 54 | //2.获取钱包地址 根据公钥生成地址 55 | func (wallet *Wallet) GetAddress() []byte { 56 | 57 | //1.使用RIPEMD160(SHA256(PubKey)) 哈希算法,取公钥并对其哈希两次 58 | ripemd160Hash := Ripemd160Hash(wallet.PublicKey) 59 | //2.拼接版本 60 | version_ripemd160Hash := append([]byte{Version}, ripemd160Hash...) 61 | //3.两次sha256生成校验和 62 | checkSumBytes := CheckSum(version_ripemd160Hash) 63 | //4.拼接校验和 64 | bytes := append(version_ripemd160Hash, checkSumBytes...) 65 | 66 | //5.base58编码 67 | return Base58Encode(bytes) 68 | } 69 | 70 | //将公钥进行两次哈希 71 | func Ripemd160Hash(publicKey []byte) []byte { 72 | 73 | //1.hash256 74 | hash256 := sha256.New() 75 | hash256.Write(publicKey) 76 | hash := hash256.Sum(nil) 77 | 78 | //2.ripemd160 79 | ripemd160 := ripemd160.New() 80 | ripemd160.Write(hash) 81 | 82 | return ripemd160.Sum(nil) 83 | } 84 | 85 | //两次sha256哈希生成校验和 86 | func CheckSum(bytes []byte) []byte { 87 | 88 | //hasher := sha256.New() 89 | //hasher.Write(bytes) 90 | //hash := hasher.Sum(nil) 91 | //与下面一句等同 92 | //hash := sha256.Sum256(bytes) 93 | 94 | hash1 := sha256.Sum256(bytes) 95 | hash2 := sha256.Sum256(hash1[:]) 96 | 97 | return hash2[:AddressChecksumLen] 98 | } 99 | 100 | //3.判断地址是否有效 101 | func (wallet *Wallet) IsValidForAddress(address []byte) bool { 102 | 103 | //1.base58解码地址得到版本,公钥哈希和校验位拼接的字节数组 104 | version_publicKey_checksumBytes := Base58Decode(address) 105 | //2.获取校验位和version_publicKeHash 106 | checkSumBytes := version_publicKey_checksumBytes[len(version_publicKey_checksumBytes)-AddressChecksumLen:] 107 | version_ripemd160 := version_publicKey_checksumBytes[:len(version_publicKey_checksumBytes)-AddressChecksumLen] 108 | 109 | //3.重新用解码后的version_ripemd160获得校验和 110 | checkSumBytesNew := CheckSum(version_ripemd160) 111 | 112 | //4.比较解码生成的校验和CheckSum重新计算的校验和 113 | if bytes.Compare(checkSumBytes, checkSumBytesNew) == 0 { 114 | 115 | return true 116 | } 117 | 118 | return false 119 | } -------------------------------------------------------------------------------- /part10-address_Prototype/BLC/Wallets.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "crypto/elliptic" 6 | "bytes" 7 | "log" 8 | "io/ioutil" 9 | "os" 10 | "fmt" 11 | ) 12 | 13 | const WalletFile = "Wallets.dat" 14 | 15 | type Wallets struct { 16 | Wallets map[string] *Wallet 17 | } 18 | 19 | //1.创建钱包集合 20 | func NewWallets() (*Wallets, error) { 21 | 22 | //判断文件是否存在 23 | if _, err := os.Stat(WalletFile); os.IsNotExist(err) { 24 | 25 | wallets := &Wallets{} 26 | wallets.Wallets = make(map[string] *Wallet) 27 | 28 | return wallets, err 29 | } 30 | 31 | 32 | var wallets Wallets 33 | //读取文件 34 | fileContent, err := ioutil.ReadFile(WalletFile) 35 | if err != nil { 36 | 37 | log.Panic(err) 38 | } 39 | 40 | gob.Register(elliptic.P256()) 41 | decoder := gob.NewDecoder(bytes.NewReader(fileContent)) 42 | err = decoder.Decode(&wallets) 43 | if err != nil { 44 | 45 | log.Panic(err) 46 | } 47 | 48 | return &wallets, err 49 | } 50 | 51 | //2.创建新钱包 52 | func (wallets *Wallets) CreateWallet() { 53 | 54 | wallet := NewWallet() 55 | fmt.Printf("Your new addres:%s\n",wallet.GetAddress()) 56 | wallets.Wallets[string(wallet.GetAddress())] = wallet 57 | 58 | //保存到本地 59 | wallets.SaveWallets() 60 | } 61 | 62 | //3.保存钱包集信息到文件 63 | func (wallets *Wallets) SaveWallets() { 64 | 65 | var context bytes.Buffer 66 | 67 | //注册是为了可以序列化任何类型 68 | gob.Register(elliptic.P256()) 69 | encoder :=gob.NewEncoder(&context) 70 | err := encoder.Encode(&wallets) 71 | if err != nil { 72 | 73 | log.Panic(err) 74 | } 75 | 76 | // 将序列化以后的数覆盖写入到文件 77 | err = ioutil.WriteFile(WalletFile, context.Bytes(), 0664) 78 | if err != nil { 79 | 80 | log.Panic(err) 81 | } 82 | } -------------------------------------------------------------------------------- /part10-address_Prototype/Wallets.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part10-address_Prototype/Wallets.dat -------------------------------------------------------------------------------- /part10-address_Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part10-address_Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part10-address_Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part10-address_Prototype/main -------------------------------------------------------------------------------- /part10-address_Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part10-address_Prototype/BLC" 6 | ) 7 | 8 | func main() { 9 | 10 | cli := BLC.CLI{} 11 | cli.Run() 12 | 13 | /** 14 | //Wallet 15 | ripemd160.New() 16 | wallet := BLC.NewWallet() 17 | address := wallet.GetAddress() 18 | 19 | fmt.Printf("%s\n", address) 20 | //1CiS8axkfLGQUYaeZsuS2Fpv4nVcd6HQqk 和比特币地址相同,可以blockchaininfo查询余额 21 | //当然一定为0 22 | 23 | //判断地址有效性 24 | fmt.Println(wallet.IsValidForAddress(address)) 25 | //修改address 26 | fmt.Println(wallet.IsValidForAddress([]byte("1CiS8axkfLGQUYaeZsuS2Fpv4nVcd6HQqkk"))) 27 | 28 | //Wallets 29 | wallets := BLC.NewWallets() 30 | wallets.CreateWallet() 31 | wallets.CreateWallet() 32 | wallets.CreateWallet() 33 | fmt.Println() 34 | fmt.Println(wallets) 35 | 36 | //blc := BLC.CreateBlockchainWithGensisBlock("chaors") 37 | //utxos := blc.UnUTXOs("chaors") 38 | //fmt.Println(utxos) 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /part10-address_Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 转账 3 | 1.sha256 base58 4 | 2.公私钥 5 | 3.address 6 | 4.Wallets 7 | 5.集成到之前的项目 cli createWallet send 8 | getAddressList(目前每次创建都是一个新的钱包集,需要将创建的钱包集持久化) 9 | 6.TXInput UnlockWithAddress完全可以传addresss啊???utxo方法 10 | 8.Signature 11 | 9.挖矿奖励 12 | **/ -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/Base58.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | ) 7 | 8 | /** 9 | Base58是用于比特币中使用的一种独特的编码方式,主要用于产生比特币的钱包地址。 10 | 11 | 相比的Base64,Base58不使用数字 “0”,字母大写 “O”,字母大写 “I”,和字母小写 “L”,以及 “+” 和 “/” 符号。 12 | 13 | 设计Base58主要的目的是: 14 | 15 | 避免混淆。在某些字体下,数字0和字母大写O,以及字母大写我和字母小写升会非常相似。 16 | 不使用 “+” 和 “/” 的原因是非字母或数字的字符串作为帐号较难被接受。 17 | 没有标点符号,通常不会被从中间分行。 18 | 大部分的软件支持双击选择整个字符串。 19 | 但是这个base58的计算量比BASE64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。 20 | 而且长度也比的base64稍微多了一点。 21 | */ 22 | 23 | //base64:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 24 | //base58:去掉0(零),O(大写的 o),I(大写的i),l(小写的 L),+,/ 25 | 26 | //base58编码集 27 | var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 28 | 29 | // 字节数组转 Base58,加密 30 | func Base58Encode(input []byte) []byte { 31 | 32 | var result []byte 33 | 34 | x := big.NewInt(0).SetBytes(input) 35 | 36 | base := big.NewInt(int64(len(b58Alphabet))) 37 | zero := big.NewInt(0) 38 | mod := &big.Int{} 39 | 40 | for x.Cmp(zero) != 0 { 41 | 42 | x.DivMod(x, base, mod) 43 | result = append(result, b58Alphabet[mod.Int64()]) 44 | } 45 | 46 | ReverseBytes(result) 47 | for b := range input { 48 | 49 | if b == 0x00 { 50 | 51 | result = append([]byte{b58Alphabet[0]}, result...) 52 | } else { 53 | 54 | break 55 | } 56 | } 57 | 58 | return result 59 | } 60 | 61 | // Base58转字节数组,解密 62 | func Base58Decode(input []byte) []byte { 63 | 64 | result := big.NewInt(0) 65 | zeroBytes := 0 66 | 67 | for b := range input { 68 | 69 | if b == 0x00 { 70 | 71 | zeroBytes++ 72 | } 73 | } 74 | 75 | payload := input[zeroBytes:] 76 | for _, b := range payload { 77 | 78 | charIndex := bytes.IndexByte(b58Alphabet, b) 79 | result.Mul(result, big.NewInt(58)) 80 | result.Add(result, big.NewInt(int64(charIndex))) 81 | } 82 | 83 | decoded := result.Bytes() 84 | //decoded...表示将decoded所有字节追加 85 | decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...) 86 | 87 | return decoded 88 | } 89 | 90 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | "crypto/sha256" 20 | ) 21 | 22 | type Block struct { 23 | //1.区块高度 24 | Height int64 25 | //2.上一个区块HAsh 26 | PrevBlockHash []byte 27 | //3.交易数据 28 | Txs [] *Transaction 29 | //4.时间戳 30 | Timestamp int64 31 | //5.Hash 32 | Hash []byte 33 | //6.Nonce 符合工作量证明的随机数 34 | Nonce int64 35 | } 36 | 37 | //区块序列化 38 | func (block *Block) Serialize() []byte { 39 | 40 | var result bytes.Buffer 41 | 42 | encoder := gob.NewEncoder(&result) 43 | 44 | err := encoder.Encode(block) 45 | if err != nil { 46 | log.Panic(err) 47 | } 48 | 49 | return result.Bytes() 50 | } 51 | 52 | //区块反序列化 53 | func DeSerializeBlock(blockBytes []byte) *Block { 54 | 55 | var block Block 56 | 57 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 58 | 59 | err := decoder.Decode(&block) 60 | 61 | if err != nil { 62 | 63 | log.Panic(err) 64 | } 65 | 66 | return &block 67 | } 68 | 69 | 70 | //1.创建新的区块 71 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 72 | 73 | //创建区块 74 | block := &Block{ 75 | Height: height, 76 | PrevBlockHash: prevBlockHash, 77 | Txs: txs, 78 | Timestamp: time.Now().Unix(), 79 | Hash: nil, 80 | Nonce: 0} 81 | 82 | //调用工作量证明返回有效的Hash 83 | pow := NewProofOfWork(block) 84 | hash, nonce := pow.Run() 85 | block.Hash = hash[:] 86 | block.Nonce = nonce 87 | 88 | fmt.Printf("\r######%d-%x\n", nonce, hash) 89 | 90 | return block 91 | } 92 | 93 | //单独方法生成创世区块 94 | func CreateGenesisBlock(txs []*Transaction) *Block { 95 | 96 | return NewBlock( 97 | txs, 98 | 1, 99 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 100 | ) 101 | } 102 | 103 | // 需要将Txs转换成[]byte 104 | func (block *Block) HashTransactions() []byte { 105 | 106 | var txHashes [][]byte 107 | var txHash [32]byte 108 | 109 | for _, tx := range block.Txs { 110 | 111 | txHashes = append(txHashes, tx.TxHAsh) 112 | } 113 | txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 114 | 115 | return txHash[:] 116 | 117 | } 118 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error{ 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | 24 | if b != nil { 25 | 26 | currentBlockBytes := b.Get(blcIterator.CurrHash) 27 | 28 | // 获取到当前迭代器里面的currentHash所对应的区块 29 | block = DeSerializeBlock(currentBlockBytes) 30 | 31 | // 更新迭代器里面CurrentHash 32 | blcIterator.CurrHash = block.PrevBlockHash 33 | } 34 | 35 | return nil 36 | }) 37 | if err != nil { 38 | log.Panic(err) 39 | } 40 | 41 | return block 42 | } 43 | 44 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | "github.com/blockchain_go_videos-master/part53-wallets/BLC" 9 | ) 10 | 11 | type CLI struct { 12 | 13 | } 14 | 15 | //打印目前左右命令使用方法 16 | func printUsage() { 17 | fmt.Println("Usage:") 18 | fmt.Println("\tcreateBlockchain -address --创世区块地址 ") 19 | fmt.Println("\tsend -from FROM -to TO -amount AMOUNT --交易明细") 20 | fmt.Println("\tprintchain --打印所有区块信息") 21 | fmt.Println("\tgetbalance -address -- 输出区块信息.") 22 | fmt.Println("\tcreateWallet -- 创建钱包.") 23 | fmt.Println("\tgetAddressList -- 输出所有钱包地址.") 24 | fmt.Println("\ttest -- 测试UTXOSet.") 25 | } 26 | 27 | func isValidArgs() { 28 | 29 | //获取当前输入参数个数 30 | if len(os.Args) < 2 { 31 | printUsage() 32 | os.Exit(1) 33 | } 34 | } 35 | 36 | func (cli *CLI) Run() { 37 | 38 | isValidArgs() 39 | 40 | //自定义cli命令 41 | sendBlockCmd := flag.NewFlagSet("send", flag.ExitOnError) 42 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 43 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 44 | blanceBlockCmd := flag.NewFlagSet("getBalance", flag.ExitOnError) 45 | createWalletCmd := flag.NewFlagSet("createWallet", flag.ExitOnError) 46 | getAddressListCmd := flag.NewFlagSet("getAddressList", flag.ExitOnError) 47 | testCmd := flag.NewFlagSet("test", flag.ExitOnError) 48 | 49 | //addBlockCmd 设置默认参数 50 | flagSendBlockFrom := sendBlockCmd.String("from", "", "源地址") 51 | flagSendBlockTo := sendBlockCmd.String("to", "", "目标地址") 52 | flagSendBlockAmount := sendBlockCmd.String("amount", "", "转账金额") 53 | flagCreateBlockchainAddress := createBlockchainCmd.String("address", "", "创世区块地址") 54 | flagBlanceBlockAddress := blanceBlockCmd.String("address", "", "输出区块信息") 55 | 56 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 57 | switch os.Args[1] { 58 | case "send": 59 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 60 | err := sendBlockCmd.Parse(os.Args[2:]) 61 | if err != nil { 62 | log.Panic(err) 63 | } 64 | case "printchain": 65 | err := printchainCmd.Parse(os.Args[2:]) 66 | if err != nil { 67 | log.Panic(err) 68 | } 69 | case "createBlockchain": 70 | err := createBlockchainCmd.Parse(os.Args[2:]) 71 | if err != nil { 72 | log.Panic(err) 73 | } 74 | case "getBalance": 75 | err := blanceBlockCmd.Parse(os.Args[2:]) 76 | if err != nil { 77 | log.Panic(err) 78 | } 79 | case "createWallet": 80 | err := createWalletCmd.Parse(os.Args[2:]) 81 | if err != nil { 82 | log.Panic(err) 83 | } 84 | case "getAddressList": 85 | err := getAddressListCmd.Parse(os.Args[2:]) 86 | if err != nil { 87 | log.Panic(err) 88 | } 89 | case "test": 90 | err := testCmd.Parse(os.Args[2:]) 91 | if err != nil { 92 | log.Panic(err) 93 | } 94 | default: 95 | printUsage() 96 | os.Exit(1) 97 | } 98 | 99 | //对addBlockCmd命令的解析 100 | if sendBlockCmd.Parsed() { 101 | 102 | if *flagSendBlockFrom == "" { 103 | 104 | printUsage() 105 | os.Exit(1) 106 | } 107 | if *flagSendBlockTo == "" { 108 | 109 | printUsage() 110 | os.Exit(1) 111 | } 112 | if *flagSendBlockAmount == "" { 113 | 114 | printUsage() 115 | os.Exit(1) 116 | } 117 | 118 | //cli.addBlock(*flagAddBlockData) 119 | 120 | //这里真正地调用转账方法 121 | from := Json2Array(*flagSendBlockFrom) 122 | to := Json2Array(*flagSendBlockTo) 123 | 124 | //输入地址有效性判断 125 | for index, fromAddress := range from { 126 | 127 | if BLC.IsValidForAdress([]byte(fromAddress)) == false || BLC.IsValidForAdress([]byte(to[index])) == false { 128 | 129 | fmt.Printf("Address:%s incalid", fromAddress) 130 | os.Exit(1) 131 | } 132 | } 133 | 134 | amount := Json2Array(*flagSendBlockAmount) 135 | 136 | cli.send(from, to, amount) 137 | } 138 | //对printchainCmd命令的解析 139 | if printchainCmd.Parsed() { 140 | 141 | cli.printchain() 142 | } 143 | //创建区块链 144 | if createBlockchainCmd.Parsed() { 145 | 146 | if *flagCreateBlockchainAddress == "" { 147 | 148 | cli.creatBlockchain(*flagCreateBlockchainAddress) 149 | } 150 | 151 | cli.creatBlockchain(*flagCreateBlockchainAddress) 152 | } 153 | 154 | //查询余额 155 | if blanceBlockCmd.Parsed() { 156 | 157 | if *flagBlanceBlockAddress == "" { 158 | 159 | printUsage() 160 | os.Exit(1) 161 | } 162 | 163 | cli.getBlance(*flagBlanceBlockAddress) 164 | } 165 | 166 | //创建钱包 167 | if createWalletCmd.Parsed() { 168 | 169 | cli.createWallet() 170 | } 171 | 172 | //获取所有钱包地址 173 | if getAddressListCmd.Parsed() { 174 | 175 | cli.getAddressList() 176 | } 177 | 178 | //UTXOSet测试 179 | if testCmd.Parsed() { 180 | 181 | cli.TestMethod() 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_creatBlockchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //新建区块链 6 | func (cli *CLI)creatBlockchain(address string) { 7 | 8 | blockchain := CreateBlockchainWithGensisBlock(address) 9 | defer blockchain.DB.Close() 10 | } 11 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_createWallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI)createWallet() { 6 | 7 | wallets, _ := NewWallets() 8 | wallets.CreateWallet() 9 | 10 | fmt.Println(len(wallets.Wallets)) 11 | } 12 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_getAddrestList.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) getAddressList() { 6 | 7 | fmt.Println("All addresses:") 8 | 9 | wallets, _ := NewWallets() 10 | for address, _ := range wallets.Wallets { 11 | 12 | fmt.Println(address) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_getBlance.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | 6 | //查询余额 7 | func (cli *CLI) getBlance(address string) { 8 | 9 | fmt.Println("地址:" + address) 10 | 11 | blockchain := GetBlockchain() 12 | defer blockchain.DB.Close() 13 | 14 | //amount := blockchain.GetBalance(address) 引入UTXOSet前的方法 15 | 16 | utxoSet := &UTXOSet{blockchain} 17 | amount := utxoSet.GetBalance(address) 18 | 19 | fmt.Printf("%s一共有%d个Token\n", address, amount) 20 | } 21 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_printchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | //打印区块链 5 | func (cli *CLI) printchain() { 6 | 7 | blockchain := GetBlockchain() 8 | defer blockchain.DB.Close() 9 | 10 | blockchain.Printchain() 11 | } 12 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_send.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //转账 6 | func (cli *CLI) send(from []string, to []string, amount []string) { 7 | 8 | blockchain := GetBlockchain() 9 | defer blockchain.DB.Close() 10 | 11 | //打包交易并挖矿 12 | blockchain.MineNewBlock(from, to, amount) 13 | 14 | //转账成功以后,需要更新UTXOSet 15 | utxoSet := &UTXOSet{blockchain} 16 | utxoSet.Update() 17 | } -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/CLI_testMethod.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) TestMethod() { 6 | 7 | 8 | fmt.Println("TestMethod") 9 | 10 | blockchain := GetBlockchain() 11 | defer blockchain.DB.Close() 12 | 13 | utxoSet := &UTXOSet{blockchain} 14 | utxoSet.ResetUTXOSet() 15 | 16 | fmt.Println(blockchain.FindUTXOMap()) 17 | } 18 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "bytes" 4 | 5 | type TXInput struct { 6 | //交易ID 7 | TxHash []byte 8 | //存储TXOutput在Vouts里的索引 9 | Vout int 10 | //数字签名 11 | Signature []byte 12 | //公钥 13 | PublicKey []byte 14 | } 15 | 16 | //验证当前输入是否是当前地址的 17 | func (txInput *TXInput) UnlockWithAddress(address string) bool { 18 | 19 | //base58解码 20 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 21 | //去除版本得到地反编码的公钥两次哈希后的值 22 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 23 | 24 | //Ripemd160Hash算法得到公钥两次哈希后的值 25 | ripemd160HashNew := Ripemd160Hash(txInput.PublicKey) 26 | 27 | return bytes.Compare(ripemd160HashNew, ripemd160Hash) == 0 28 | } -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type TXOutput struct { 8 | //面值 9 | Value int64 10 | //用户名 11 | Ripemd160Hash []byte //用户名 公钥两次哈希后的值 12 | } 13 | 14 | func NewTXOutput(value int64,address string) *TXOutput { 15 | 16 | txOutput := &TXOutput{value,nil} 17 | 18 | // 设置Ripemd160Hash 19 | txOutput.Lock(address) 20 | 21 | return txOutput 22 | } 23 | 24 | //锁定 25 | func (txOutput *TXOutput) Lock(address string) { 26 | 27 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 28 | txOutput.Ripemd160Hash = version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 29 | } 30 | 31 | //解锁 32 | func (txOutput *TXOutput) UnLockScriptPubKeyWithAddress(address string) bool { 33 | 34 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 35 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes) - 4] 36 | 37 | //fmt.Println(txOutput.Ripemd160Hash, ripemd160Hash) 38 | return bytes.Compare(txOutput.Ripemd160Hash, ripemd160Hash) == 0 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/TXOutputs.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "log" 6 | "bytes" 7 | ) 8 | 9 | type TXOutputs struct { 10 | 11 | UTXOS []*UTXO 12 | } 13 | 14 | 15 | // 序列化成字节数组 16 | func (txOutputs *TXOutputs) Serialize() []byte { 17 | 18 | var result bytes.Buffer 19 | 20 | encoder := gob.NewEncoder(&result) 21 | 22 | err := encoder.Encode(txOutputs) 23 | if err != nil { 24 | log.Panic(err) 25 | } 26 | 27 | return result.Bytes() 28 | } 29 | 30 | // 反序列化 31 | func DeserializeTXOutputs(txOutputsBytes []byte) *TXOutputs { 32 | 33 | var txOutputs TXOutputs 34 | 35 | decoder := gob.NewDecoder(bytes.NewReader(txOutputsBytes)) 36 | err := decoder.Decode(&txOutputs) 37 | if err != nil { 38 | 39 | log.Panic(err) 40 | } 41 | 42 | return &txOutputs 43 | } -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/UTXO.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type UTXO struct { 4 | //来自交易的哈希 5 | TxHash []byte 6 | //在该交易VOuts里的下标 7 | Index int 8 | //未花费的交易输出 9 | Output *TXOutput 10 | } 11 | 12 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/Utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | "encoding/json" 19 | ) 20 | 21 | //将int64转换为bytes 22 | func IntToHex(num int64) []byte { 23 | 24 | buff := new(bytes.Buffer) 25 | err := binary.Write(buff, binary.BigEndian, num) 26 | if err != nil { 27 | 28 | log.Panic(err) 29 | } 30 | 31 | return buff.Bytes() 32 | } 33 | 34 | // 标准的JSON字符串转数组 35 | func Json2Array(jsonString string) []string { 36 | 37 | //json 到 []string 38 | var sArr []string 39 | if err := json.Unmarshal([]byte(jsonString), &sArr); err != nil { 40 | 41 | log.Panic(err) 42 | } 43 | return sArr 44 | } 45 | 46 | 47 | // 字节数组反转 48 | func ReverseBytes(data []byte) { 49 | 50 | for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 { 51 | 52 | data[i], data[j] = data[j], data[i] 53 | } 54 | } -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/Wallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "log" 8 | "crypto/sha256" 9 | "golang.org/x/crypto/ripemd160" 10 | "bytes" 11 | ) 12 | 13 | //用于生成地址的版本 14 | const Version = byte(0x00) 15 | //用于生成地址的校验和位数 16 | const AddressChecksumLen = 4 17 | 18 | 19 | type Wallet struct { 20 | //私钥 21 | PrivateKey ecdsa.PrivateKey 22 | //公钥 23 | PublicKey []byte 24 | } 25 | 26 | //1.创建钱包 27 | func NewWallet () *Wallet { 28 | 29 | privateKey, publicKey := newKeyPair() 30 | 31 | return &Wallet{privateKey, publicKey} 32 | } 33 | 34 | //通过私钥创建公钥 35 | func newKeyPair() (ecdsa.PrivateKey, []byte) { 36 | 37 | //1.椭圆曲线算法生成私钥 38 | curve := elliptic.P256() 39 | privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) 40 | if err != nil { 41 | 42 | log.Panic(err) 43 | } 44 | 45 | //2.通过私钥生成公钥 46 | publicKey := append(privateKey.PublicKey.X.Bytes(), privateKey.PublicKey.Y.Bytes()...) 47 | 48 | 49 | return *privateKey, publicKey 50 | } 51 | 52 | //2.获取钱包地址 根据公钥生成地址 53 | func (wallet *Wallet) GetAddress() []byte { 54 | 55 | //1.使用RIPEMD160(SHA256(PubKey)) 哈希算法,取公钥并对其哈希两次 56 | ripemd160Hash := Ripemd160Hash(wallet.PublicKey) 57 | //2.拼接版本 58 | version_ripemd160Hash := append([]byte{Version}, ripemd160Hash...) 59 | //3.两次sha256生成校验和 60 | checkSumBytes := CheckSum(version_ripemd160Hash) 61 | //4.拼接校验和 62 | bytes := append(version_ripemd160Hash, checkSumBytes...) 63 | 64 | //5.base58编码 65 | return Base58Encode(bytes) 66 | } 67 | 68 | //将公钥进行两次哈希 69 | func Ripemd160Hash(publicKey []byte) []byte { 70 | 71 | //1.hash256 72 | hash256 := sha256.New() 73 | hash256.Write(publicKey) 74 | hash := hash256.Sum(nil) 75 | 76 | //2.ripemd160 77 | ripemd160 := ripemd160.New() 78 | ripemd160.Write(hash) 79 | 80 | return ripemd160.Sum(nil) 81 | } 82 | 83 | //两次sha256哈希生成校验和 84 | func CheckSum(bytes []byte) []byte { 85 | 86 | //hasher := sha256.New() 87 | //hasher.Write(bytes) 88 | //hash := hasher.Sum(nil) 89 | //与下面一句等同 90 | //hash := sha256.Sum256(bytes) 91 | 92 | hash1 := sha256.Sum256(bytes) 93 | hash2 := sha256.Sum256(hash1[:]) 94 | 95 | return hash2[:AddressChecksumLen] 96 | } 97 | 98 | //3.判断地址是否有效 99 | func IsValidForAddress(address []byte) bool { 100 | 101 | //1.base58解码地址得到版本,公钥哈希和校验位拼接的字节数组 102 | version_publicKey_checksumBytes := Base58Decode(address) 103 | //2.获取校验位和version_publicKeHash 104 | checkSumBytes := version_publicKey_checksumBytes[len(version_publicKey_checksumBytes)-AddressChecksumLen:] 105 | version_ripemd160 := version_publicKey_checksumBytes[:len(version_publicKey_checksumBytes)-AddressChecksumLen] 106 | 107 | //3.重新用解码后的version_ripemd160获得校验和 108 | checkSumBytesNew := CheckSum(version_ripemd160) 109 | 110 | //4.比较解码生成的校验和CheckSum重新计算的校验和 111 | if bytes.Compare(checkSumBytes, checkSumBytesNew) == 0 { 112 | 113 | return true 114 | } 115 | 116 | return false 117 | } -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/BLC/Wallets.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "crypto/elliptic" 6 | "bytes" 7 | "log" 8 | "io/ioutil" 9 | "os" 10 | "fmt" 11 | ) 12 | 13 | //存储钱包集的文件名 14 | const WalletFile = "Wallets.dat" 15 | 16 | type Wallets struct { 17 | Wallets map[string] *Wallet 18 | } 19 | 20 | //1.创建钱包集合 21 | func NewWallets() (*Wallets, error) { 22 | 23 | //判断文件是否存在 24 | if _, err := os.Stat(WalletFile); os.IsNotExist(err) { 25 | 26 | wallets := &Wallets{} 27 | wallets.Wallets = make(map[string] *Wallet) 28 | 29 | return wallets, err 30 | } 31 | 32 | 33 | var wallets Wallets 34 | //读取文件 35 | fileContent, err := ioutil.ReadFile(WalletFile) 36 | if err != nil { 37 | 38 | log.Panic(err) 39 | } 40 | 41 | gob.Register(elliptic.P256()) 42 | decoder := gob.NewDecoder(bytes.NewReader(fileContent)) 43 | err = decoder.Decode(&wallets) 44 | if err != nil { 45 | 46 | log.Panic(err) 47 | } 48 | 49 | return &wallets, err 50 | } 51 | 52 | //2.创建新钱包 53 | func (wallets *Wallets) CreateWallet() { 54 | 55 | wallet := NewWallet() 56 | fmt.Printf("Your new addres:%s\n",wallet.GetAddress()) 57 | wallets.Wallets[string(wallet.GetAddress())] = wallet 58 | 59 | //保存到本地 60 | wallets.SaveWallets() 61 | } 62 | 63 | //3.保存钱包集信息到文件 64 | func (wallets *Wallets) SaveWallets() { 65 | 66 | var context bytes.Buffer 67 | 68 | //注册是为了可以序列化任何类型 69 | gob.Register(elliptic.P256()) 70 | encoder :=gob.NewEncoder(&context) 71 | err := encoder.Encode(&wallets) 72 | if err != nil { 73 | 74 | log.Panic(err) 75 | } 76 | 77 | // 将序列化以后的数覆盖写入到文件 78 | err = ioutil.WriteFile(WalletFile, context.Bytes(), 0664) 79 | if err != nil { 80 | 81 | log.Panic(err) 82 | } 83 | } -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/Wallets.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part11-transaction_1_Prototype/Wallets.dat -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part11-transaction_1_Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part11-transaction_1_Prototype/main -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part11-transaction_1_Prototype/BLC" 6 | ) 7 | 8 | func main() { 9 | 10 | cli := BLC.CLI{} 11 | cli.Run() 12 | 13 | //wallets := BLC.NewWallets() 14 | //address := hex.EncodeToString(wallet.GetAddress()) 15 | //wallet1 := BLC.NewWallet() 16 | //address1 := hex.EncodeToString(wallet1.GetAddress()) 17 | // 18 | //var from []string 19 | //var to []string 20 | //var amount []string 21 | //from = append(from, address) 22 | //to =append(to, address1) 23 | //amount = append(amount, "5") 24 | // 25 | //blc := BLC.CreateBlockchainWithGensisBlock(address) 26 | //blc.MineNewBlock(from, to, amount) 27 | 28 | /** 29 | //Wallet 30 | ripemd160.New() 31 | wallet := BLC.NewWallet() 32 | address := wallet.GetAddress() 33 | 34 | fmt.Printf("%s\n", address) 35 | //1CiS8axkfLGQUYaeZsuS2Fpv4nVcd6HQqk 和比特币地址相同,可以blockchaininfo查询余额 36 | //当然一定为0 37 | 38 | //判断地址有效性 39 | fmt.Println(wallet.IsValidForAddress(address)) 40 | //修改address 41 | fmt.Println(wallet.IsValidForAddress([]byte("1CiS8axkfLGQUYaeZsuS2Fpv4nVcd6HQqkk"))) 42 | 43 | //Wallets 44 | wallets := BLC.NewWallets() 45 | wallets.CreateWallet() 46 | wallets.CreateWallet() 47 | wallets.CreateWallet() 48 | fmt.Println() 49 | fmt.Println(wallets) 50 | 51 | //blc := BLC.CreateBlockchainWithGensisBlock("chaors") 52 | //utxos := blc.UnUTXOs("chaors") 53 | //fmt.Println(utxos) 54 | */ 55 | } 56 | -------------------------------------------------------------------------------- /part11-transaction_1_Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 转账 3 | 1.reward 4 | 2.UTXOSet 查询余额的时候遍历了整个数据库,怎么更简单 ResetUTXOSet创世区块时初始化 5 | 查询余额 6 | 转账改变的UTXO 7 | FindUnPackageSpendableUTXOS Set本身就是UTXO集合,里面的不全是未花费吗???? 8 | 9 | 3.修改多笔交易验签的bug,添加txs 10 | 11 | 12 | **/ -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/Base58.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | ) 7 | 8 | /** 9 | Base58是用于比特币中使用的一种独特的编码方式,主要用于产生比特币的钱包地址。 10 | 11 | 相比的Base64,Base58不使用数字 “0”,字母大写 “O”,字母大写 “I”,和字母小写 “L”,以及 “+” 和 “/” 符号。 12 | 13 | 设计Base58主要的目的是: 14 | 15 | 避免混淆。在某些字体下,数字0和字母大写O,以及字母大写我和字母小写升会非常相似。 16 | 不使用 “+” 和 “/” 的原因是非字母或数字的字符串作为帐号较难被接受。 17 | 没有标点符号,通常不会被从中间分行。 18 | 大部分的软件支持双击选择整个字符串。 19 | 但是这个base58的计算量比BASE64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。 20 | 而且长度也比的base64稍微多了一点。 21 | */ 22 | 23 | //base64:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 24 | //base58:去掉0(零),O(大写的 o),I(大写的i),l(小写的 L),+,/ 25 | 26 | //base58编码集 27 | var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 28 | 29 | // 字节数组转 Base58,加密 30 | func Base58Encode(input []byte) []byte { 31 | 32 | var result []byte 33 | 34 | x := big.NewInt(0).SetBytes(input) 35 | 36 | base := big.NewInt(int64(len(b58Alphabet))) 37 | zero := big.NewInt(0) 38 | mod := &big.Int{} 39 | 40 | for x.Cmp(zero) != 0 { 41 | 42 | x.DivMod(x, base, mod) 43 | result = append(result, b58Alphabet[mod.Int64()]) 44 | } 45 | 46 | ReverseBytes(result) 47 | for b := range input { 48 | 49 | if b == 0x00 { 50 | 51 | result = append([]byte{b58Alphabet[0]}, result...) 52 | } else { 53 | 54 | break 55 | } 56 | } 57 | 58 | return result 59 | } 60 | 61 | // Base58转字节数组,解密 62 | func Base58Decode(input []byte) []byte { 63 | 64 | result := big.NewInt(0) 65 | zeroBytes := 0 66 | 67 | for b := range input { 68 | 69 | if b == 0x00 { 70 | 71 | zeroBytes++ 72 | } 73 | } 74 | 75 | payload := input[zeroBytes:] 76 | for _, b := range payload { 77 | 78 | charIndex := bytes.IndexByte(b58Alphabet, b) 79 | result.Mul(result, big.NewInt(58)) 80 | result.Add(result, big.NewInt(int64(charIndex))) 81 | } 82 | 83 | decoded := result.Bytes() 84 | //decoded...表示将decoded所有字节追加 85 | decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...) 86 | 87 | return decoded 88 | } 89 | 90 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | ) 20 | 21 | type Block struct { 22 | //1.区块高度 23 | Height int64 24 | //2.上一个区块HAsh 25 | PrevBlockHash []byte 26 | //3.交易数据 27 | Txs [] *Transaction 28 | //4.时间戳 29 | Timestamp int64 30 | //5.Hash 31 | Hash []byte 32 | //6.Nonce 符合工作量证明的随机数 33 | Nonce int64 34 | } 35 | 36 | //区块序列化 37 | func (block *Block) Serialize() []byte { 38 | 39 | var result bytes.Buffer 40 | 41 | encoder := gob.NewEncoder(&result) 42 | 43 | err := encoder.Encode(block) 44 | if err != nil { 45 | log.Panic(err) 46 | } 47 | 48 | return result.Bytes() 49 | } 50 | 51 | //区块反序列化 52 | func DeSerializeBlock(blockBytes []byte) *Block { 53 | 54 | var block Block 55 | 56 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 57 | 58 | err := decoder.Decode(&block) 59 | 60 | if err != nil { 61 | 62 | log.Panic(err) 63 | } 64 | 65 | return &block 66 | } 67 | 68 | 69 | //1.创建新的区块 70 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 71 | 72 | //创建区块 73 | block := &Block{ 74 | Height: height, 75 | PrevBlockHash: prevBlockHash, 76 | Txs: txs, 77 | Timestamp: time.Now().Unix(), 78 | Hash: nil, 79 | Nonce: 0} 80 | 81 | //调用工作量证明返回有效的Hash 82 | pow := NewProofOfWork(block) 83 | hash, nonce := pow.Run() 84 | block.Hash = hash[:] 85 | block.Nonce = nonce 86 | 87 | fmt.Printf("\r######%d-%x\n", nonce, hash) 88 | 89 | return block 90 | } 91 | 92 | //单独方法生成创世区块 93 | func CreateGenesisBlock(txs []*Transaction) *Block { 94 | 95 | return NewBlock( 96 | txs, 97 | 1, 98 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 99 | ) 100 | } 101 | 102 | // 需要将Txs转换成[]byte 103 | func (block *Block) HashTransactions() []byte { 104 | 105 | //引入MerkleTree前的交易哈希 106 | //var txHashes [][]byte 107 | //var txHash [32]byte 108 | // 109 | //for _, tx := range block.Txs { 110 | // 111 | // txHashes = append(txHashes, tx.TxHAsh) 112 | //} 113 | //txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 114 | // 115 | //return txHash[:] 116 | 117 | //默克尔树根节点表示交易哈希 118 | var transactions [][]byte 119 | 120 | for _, tx := range block.Txs { 121 | 122 | transactions = append(transactions, tx.Serialize()) 123 | } 124 | mTree := NewMerkleTree(transactions) 125 | 126 | return mTree.RootNode.Data 127 | } 128 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error{ 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | 24 | if b != nil { 25 | 26 | currentBlockBytes := b.Get(blcIterator.CurrHash) 27 | 28 | // 获取到当前迭代器里面的currentHash所对应的区块 29 | block = DeSerializeBlock(currentBlockBytes) 30 | 31 | // 更新迭代器里面CurrentHash 32 | blcIterator.CurrHash = block.PrevBlockHash 33 | } 34 | 35 | return nil 36 | }) 37 | if err != nil { 38 | log.Panic(err) 39 | } 40 | 41 | return block 42 | } 43 | 44 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | "github.com/blockchain_go_videos-master/part53-wallets/BLC" 9 | ) 10 | 11 | type CLI struct { 12 | 13 | } 14 | 15 | //打印目前左右命令使用方法 16 | func printUsage() { 17 | fmt.Println("Usage:") 18 | fmt.Println("\tcreateBlockchain -address --创世区块地址 ") 19 | fmt.Println("\tsend -from FROM -to TO -amount AMOUNT --交易明细") 20 | fmt.Println("\tprintchain --打印所有区块信息") 21 | fmt.Println("\tgetbalance -address -- 输出区块信息.") 22 | fmt.Println("\tcreateWallet -- 创建钱包.") 23 | fmt.Println("\tgetAddressList -- 输出所有钱包地址.") 24 | fmt.Println("\ttest -- 测试UTXOSet.") 25 | } 26 | 27 | func isValidArgs() { 28 | 29 | //获取当前输入参数个数 30 | if len(os.Args) < 2 { 31 | printUsage() 32 | os.Exit(1) 33 | } 34 | } 35 | 36 | func (cli *CLI) Run() { 37 | 38 | isValidArgs() 39 | 40 | //自定义cli命令 41 | sendBlockCmd := flag.NewFlagSet("send", flag.ExitOnError) 42 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 43 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 44 | blanceBlockCmd := flag.NewFlagSet("getBalance", flag.ExitOnError) 45 | createWalletCmd := flag.NewFlagSet("createWallet", flag.ExitOnError) 46 | getAddressListCmd := flag.NewFlagSet("getAddressList", flag.ExitOnError) 47 | testCmd := flag.NewFlagSet("test", flag.ExitOnError) 48 | 49 | //addBlockCmd 设置默认参数 50 | flagSendBlockFrom := sendBlockCmd.String("from", "", "源地址") 51 | flagSendBlockTo := sendBlockCmd.String("to", "", "目标地址") 52 | flagSendBlockAmount := sendBlockCmd.String("amount", "", "转账金额") 53 | flagCreateBlockchainAddress := createBlockchainCmd.String("address", "", "创世区块地址") 54 | flagBlanceBlockAddress := blanceBlockCmd.String("address", "", "输出区块信息") 55 | 56 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 57 | switch os.Args[1] { 58 | case "send": 59 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 60 | err := sendBlockCmd.Parse(os.Args[2:]) 61 | if err != nil { 62 | log.Panic(err) 63 | } 64 | case "printchain": 65 | err := printchainCmd.Parse(os.Args[2:]) 66 | if err != nil { 67 | log.Panic(err) 68 | } 69 | case "createBlockchain": 70 | err := createBlockchainCmd.Parse(os.Args[2:]) 71 | if err != nil { 72 | log.Panic(err) 73 | } 74 | case "getBalance": 75 | err := blanceBlockCmd.Parse(os.Args[2:]) 76 | if err != nil { 77 | log.Panic(err) 78 | } 79 | case "createWallet": 80 | err := createWalletCmd.Parse(os.Args[2:]) 81 | if err != nil { 82 | log.Panic(err) 83 | } 84 | case "getAddressList": 85 | err := getAddressListCmd.Parse(os.Args[2:]) 86 | if err != nil { 87 | log.Panic(err) 88 | } 89 | case "test": 90 | err := testCmd.Parse(os.Args[2:]) 91 | if err != nil { 92 | log.Panic(err) 93 | } 94 | default: 95 | printUsage() 96 | os.Exit(1) 97 | } 98 | 99 | //对addBlockCmd命令的解析 100 | if sendBlockCmd.Parsed() { 101 | 102 | if *flagSendBlockFrom == "" { 103 | 104 | printUsage() 105 | os.Exit(1) 106 | } 107 | if *flagSendBlockTo == "" { 108 | 109 | printUsage() 110 | os.Exit(1) 111 | } 112 | if *flagSendBlockAmount == "" { 113 | 114 | printUsage() 115 | os.Exit(1) 116 | } 117 | 118 | //cli.addBlock(*flagAddBlockData) 119 | 120 | //这里真正地调用转账方法 121 | from := Json2Array(*flagSendBlockFrom) 122 | to := Json2Array(*flagSendBlockTo) 123 | 124 | //输入地址有效性判断 125 | for index, fromAddress := range from { 126 | 127 | if BLC.IsValidForAdress([]byte(fromAddress)) == false || BLC.IsValidForAdress([]byte(to[index])) == false { 128 | 129 | fmt.Printf("Address:%s incalid", fromAddress) 130 | os.Exit(1) 131 | } 132 | } 133 | 134 | amount := Json2Array(*flagSendBlockAmount) 135 | 136 | cli.send(from, to, amount) 137 | } 138 | //对printchainCmd命令的解析 139 | if printchainCmd.Parsed() { 140 | 141 | cli.printchain() 142 | } 143 | //创建区块链 144 | if createBlockchainCmd.Parsed() { 145 | 146 | if *flagCreateBlockchainAddress == "" { 147 | 148 | cli.creatBlockchain(*flagCreateBlockchainAddress) 149 | } 150 | 151 | cli.creatBlockchain(*flagCreateBlockchainAddress) 152 | } 153 | 154 | //查询余额 155 | if blanceBlockCmd.Parsed() { 156 | 157 | if *flagBlanceBlockAddress == "" { 158 | 159 | printUsage() 160 | os.Exit(1) 161 | } 162 | 163 | cli.getBlance(*flagBlanceBlockAddress) 164 | } 165 | 166 | //创建钱包 167 | if createWalletCmd.Parsed() { 168 | 169 | cli.createWallet() 170 | } 171 | 172 | //获取所有钱包地址 173 | if getAddressListCmd.Parsed() { 174 | 175 | cli.getAddressList() 176 | } 177 | 178 | //UTXOSet测试 179 | if testCmd.Parsed() { 180 | 181 | cli.TestMethod() 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_creatBlockchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //新建区块链 6 | func (cli *CLI)creatBlockchain(address string) { 7 | 8 | blockchain := CreateBlockchainWithGensisBlock(address) 9 | defer blockchain.DB.Close() 10 | } 11 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_createWallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI)createWallet() { 6 | 7 | wallets, _ := NewWallets() 8 | wallets.CreateWallet() 9 | 10 | fmt.Println(len(wallets.Wallets)) 11 | } 12 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_getAddrestList.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) getAddressList() { 6 | 7 | fmt.Println("All addresses:") 8 | 9 | wallets, _ := NewWallets() 10 | for address, _ := range wallets.Wallets { 11 | 12 | fmt.Println(address) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_getBlance.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | 6 | //查询余额 7 | func (cli *CLI) getBlance(address string) { 8 | 9 | fmt.Println("地址:" + address) 10 | 11 | blockchain := GetBlockchain() 12 | defer blockchain.DB.Close() 13 | 14 | //amount := blockchain.GetBalance(address) 引入UTXOSet前的方法 15 | 16 | utxoSet := &UTXOSet{blockchain} 17 | amount := utxoSet.GetBalance(address) 18 | 19 | fmt.Printf("%s一共有%d个Token\n", address, amount) 20 | } 21 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_printchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | //打印区块链 5 | func (cli *CLI) printchain() { 6 | 7 | blockchain := GetBlockchain() 8 | defer blockchain.DB.Close() 9 | 10 | blockchain.Printchain() 11 | } 12 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_send.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //转账 6 | func (cli *CLI) send(from []string, to []string, amount []string) { 7 | 8 | blockchain := GetBlockchain() 9 | defer blockchain.DB.Close() 10 | 11 | //打包交易并挖矿 12 | blockchain.MineNewBlock(from, to, amount) 13 | 14 | //转账成功以后,需要更新UTXOSet 15 | utxoSet := &UTXOSet{blockchain} 16 | utxoSet.Update() 17 | } -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/CLI_testMethod.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) TestMethod() { 6 | 7 | 8 | fmt.Println("TestMethod") 9 | 10 | blockchain := GetBlockchain() 11 | defer blockchain.DB.Close() 12 | 13 | utxoSet := &UTXOSet{blockchain} 14 | utxoSet.ResetUTXOSet() 15 | 16 | fmt.Println(blockchain.FindUTXOMap()) 17 | } 18 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/MerkleTree.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "crypto/sha256" 4 | 5 | //默克尔树 6 | type MerkleTree struct { 7 | //根节点 8 | RootNode *MerkleNode 9 | } 10 | 11 | //默克尔树节点 12 | type MerkleNode struct { 13 | //做节点 14 | Left *MerkleNode 15 | //右节点 16 | Right *MerkleNode 17 | //节点数据 18 | Data []byte 19 | } 20 | 21 | //新建节点 22 | func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { 23 | 24 | mNode := MerkleNode{} 25 | 26 | if left == nil && right == nil { 27 | 28 | hash := sha256.Sum256(data) 29 | mNode.Data = hash[:] 30 | } else { 31 | 32 | prevHashes := append(left.Data, right.Data...) 33 | hash := sha256.Sum256(prevHashes) 34 | mNode.Data = hash[:] 35 | } 36 | 37 | mNode.Left = left 38 | mNode.Right = right 39 | 40 | return &mNode 41 | } 42 | 43 | // 1 2 3 --> 1 2 3 3 44 | //新建默克尔树 45 | func NewMerkleTree(datas [][]byte) *MerkleTree { 46 | 47 | var nodes []*MerkleNode 48 | 49 | //如果是奇数,添加最后一个交易哈希拼凑为偶数个交易 50 | if len(datas) % 2 != 0 { 51 | 52 | datas = append(datas, datas[len(datas)-1]) 53 | } 54 | 55 | //将每一个交易哈希构造为默克尔树节点 56 | for _, data := range datas { 57 | 58 | node := NewMerkleNode(nil, nil, data) 59 | nodes = append(nodes, node) 60 | } 61 | 62 | //将所有节点两两组合生成新节点,直到最后只有一个更节点 63 | for i := 0; i < len(datas)/2; i++ { 64 | 65 | var newLevel []*MerkleNode 66 | 67 | for j := 0; j < len(nodes); j += 2 { 68 | 69 | node := NewMerkleNode(nodes[j], nodes[j+1], nil) 70 | newLevel = append(newLevel, node) 71 | } 72 | 73 | nodes = newLevel 74 | } 75 | 76 | //取根节点返回 77 | mTree := MerkleTree{nodes[0]} 78 | 79 | return &mTree 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "bytes" 4 | 5 | type TXInput struct { 6 | //交易ID 7 | TxHash []byte 8 | //存储TXOutput在Vouts里的索引 9 | Vout int 10 | //数字签名 11 | Signature []byte 12 | //公钥 13 | PublicKey []byte 14 | } 15 | 16 | //验证当前输入是否是当前地址的 17 | func (txInput *TXInput) UnlockWithAddress(address string) bool { 18 | 19 | //base58解码 20 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 21 | //去除版本得到地反编码的公钥两次哈希后的值 22 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 23 | 24 | //Ripemd160Hash算法得到公钥两次哈希后的值 25 | ripemd160HashNew := Ripemd160Hash(txInput.PublicKey) 26 | 27 | return bytes.Compare(ripemd160HashNew, ripemd160Hash) == 0 28 | } -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type TXOutput struct { 8 | //面值 9 | Value int64 10 | //用户名 11 | Ripemd160Hash []byte //用户名 公钥两次哈希后的值 12 | } 13 | 14 | func NewTXOutput(value int64,address string) *TXOutput { 15 | 16 | txOutput := &TXOutput{value,nil} 17 | 18 | // 设置Ripemd160Hash 19 | txOutput.Lock(address) 20 | 21 | return txOutput 22 | } 23 | 24 | //锁定 25 | func (txOutput *TXOutput) Lock(address string) { 26 | 27 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 28 | txOutput.Ripemd160Hash = version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 29 | } 30 | 31 | //解锁 32 | func (txOutput *TXOutput) UnLockScriptPubKeyWithAddress(address string) bool { 33 | 34 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 35 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes) - 4] 36 | 37 | //fmt.Println(txOutput.Ripemd160Hash, ripemd160Hash) 38 | return bytes.Compare(txOutput.Ripemd160Hash, ripemd160Hash) == 0 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/TXOutputs.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "log" 6 | "bytes" 7 | ) 8 | 9 | type TXOutputs struct { 10 | 11 | UTXOS []*UTXO 12 | } 13 | 14 | 15 | // 序列化成字节数组 16 | func (txOutputs *TXOutputs) Serialize() []byte { 17 | 18 | var result bytes.Buffer 19 | 20 | encoder := gob.NewEncoder(&result) 21 | 22 | err := encoder.Encode(txOutputs) 23 | if err != nil { 24 | log.Panic(err) 25 | } 26 | 27 | return result.Bytes() 28 | } 29 | 30 | // 反序列化 31 | func DeserializeTXOutputs(txOutputsBytes []byte) *TXOutputs { 32 | 33 | var txOutputs TXOutputs 34 | 35 | decoder := gob.NewDecoder(bytes.NewReader(txOutputsBytes)) 36 | err := decoder.Decode(&txOutputs) 37 | if err != nil { 38 | 39 | log.Panic(err) 40 | } 41 | 42 | return &txOutputs 43 | } -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/UTXO.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type UTXO struct { 4 | //来自交易的哈希 5 | TxHash []byte 6 | //在该交易VOuts里的下标 7 | Index int 8 | //未花费的交易输出 9 | Output *TXOutput 10 | } 11 | 12 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/Utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | "encoding/json" 19 | ) 20 | 21 | //将int64转换为bytes 22 | func IntToHex(num int64) []byte { 23 | 24 | buff := new(bytes.Buffer) 25 | err := binary.Write(buff, binary.BigEndian, num) 26 | if err != nil { 27 | 28 | log.Panic(err) 29 | } 30 | 31 | return buff.Bytes() 32 | } 33 | 34 | // 标准的JSON字符串转数组 35 | func Json2Array(jsonString string) []string { 36 | 37 | //json 到 []string 38 | var sArr []string 39 | if err := json.Unmarshal([]byte(jsonString), &sArr); err != nil { 40 | 41 | log.Panic(err) 42 | } 43 | return sArr 44 | } 45 | 46 | 47 | // 字节数组反转 48 | func ReverseBytes(data []byte) { 49 | 50 | for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 { 51 | 52 | data[i], data[j] = data[j], data[i] 53 | } 54 | } -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/Wallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "log" 8 | "crypto/sha256" 9 | "golang.org/x/crypto/ripemd160" 10 | "bytes" 11 | ) 12 | 13 | //用于生成地址的版本 14 | const Version = byte(0x00) 15 | //用于生成地址的校验和位数 16 | const AddressChecksumLen = 4 17 | 18 | 19 | type Wallet struct { 20 | //私钥 21 | PrivateKey ecdsa.PrivateKey 22 | //公钥 23 | PublicKey []byte 24 | } 25 | 26 | //1.创建钱包 27 | func NewWallet () *Wallet { 28 | 29 | privateKey, publicKey := newKeyPair() 30 | 31 | return &Wallet{privateKey, publicKey} 32 | } 33 | 34 | //通过私钥创建公钥 35 | func newKeyPair() (ecdsa.PrivateKey, []byte) { 36 | 37 | //1.椭圆曲线算法生成私钥 38 | curve := elliptic.P256() 39 | privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) 40 | if err != nil { 41 | 42 | log.Panic(err) 43 | } 44 | 45 | //2.通过私钥生成公钥 46 | publicKey := append(privateKey.PublicKey.X.Bytes(), privateKey.PublicKey.Y.Bytes()...) 47 | 48 | 49 | return *privateKey, publicKey 50 | } 51 | 52 | //2.获取钱包地址 根据公钥生成地址 53 | func (wallet *Wallet) GetAddress() []byte { 54 | 55 | //1.使用RIPEMD160(SHA256(PubKey)) 哈希算法,取公钥并对其哈希两次 56 | ripemd160Hash := Ripemd160Hash(wallet.PublicKey) 57 | //2.拼接版本 58 | version_ripemd160Hash := append([]byte{Version}, ripemd160Hash...) 59 | //3.两次sha256生成校验和 60 | checkSumBytes := CheckSum(version_ripemd160Hash) 61 | //4.拼接校验和 62 | bytes := append(version_ripemd160Hash, checkSumBytes...) 63 | 64 | //5.base58编码 65 | return Base58Encode(bytes) 66 | } 67 | 68 | //将公钥进行两次哈希 69 | func Ripemd160Hash(publicKey []byte) []byte { 70 | 71 | //1.hash256 72 | hash256 := sha256.New() 73 | hash256.Write(publicKey) 74 | hash := hash256.Sum(nil) 75 | 76 | //2.ripemd160 77 | ripemd160 := ripemd160.New() 78 | ripemd160.Write(hash) 79 | 80 | return ripemd160.Sum(nil) 81 | } 82 | 83 | //两次sha256哈希生成校验和 84 | func CheckSum(bytes []byte) []byte { 85 | 86 | //hasher := sha256.New() 87 | //hasher.Write(bytes) 88 | //hash := hasher.Sum(nil) 89 | //与下面一句等同 90 | //hash := sha256.Sum256(bytes) 91 | 92 | hash1 := sha256.Sum256(bytes) 93 | hash2 := sha256.Sum256(hash1[:]) 94 | 95 | return hash2[:AddressChecksumLen] 96 | } 97 | 98 | //3.判断地址是否有效 99 | func IsValidForAddress(address []byte) bool { 100 | 101 | //1.base58解码地址得到版本,公钥哈希和校验位拼接的字节数组 102 | version_publicKey_checksumBytes := Base58Decode(address) 103 | //2.获取校验位和version_publicKeHash 104 | checkSumBytes := version_publicKey_checksumBytes[len(version_publicKey_checksumBytes)-AddressChecksumLen:] 105 | version_ripemd160 := version_publicKey_checksumBytes[:len(version_publicKey_checksumBytes)-AddressChecksumLen] 106 | 107 | //3.重新用解码后的version_ripemd160获得校验和 108 | checkSumBytesNew := CheckSum(version_ripemd160) 109 | 110 | //4.比较解码生成的校验和CheckSum重新计算的校验和 111 | if bytes.Compare(checkSumBytes, checkSumBytesNew) == 0 { 112 | 113 | return true 114 | } 115 | 116 | return false 117 | } -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/BLC/Wallets.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "crypto/elliptic" 6 | "bytes" 7 | "log" 8 | "io/ioutil" 9 | "os" 10 | "fmt" 11 | ) 12 | 13 | //存储钱包集的文件名 14 | const WalletFile = "Wallets.dat" 15 | 16 | type Wallets struct { 17 | Wallets map[string] *Wallet 18 | } 19 | 20 | //1.创建钱包集合 21 | func NewWallets() (*Wallets, error) { 22 | 23 | //判断文件是否存在 24 | if _, err := os.Stat(WalletFile); os.IsNotExist(err) { 25 | 26 | wallets := &Wallets{} 27 | wallets.Wallets = make(map[string] *Wallet) 28 | 29 | return wallets, err 30 | } 31 | 32 | 33 | var wallets Wallets 34 | //读取文件 35 | fileContent, err := ioutil.ReadFile(WalletFile) 36 | if err != nil { 37 | 38 | log.Panic(err) 39 | } 40 | 41 | gob.Register(elliptic.P256()) 42 | decoder := gob.NewDecoder(bytes.NewReader(fileContent)) 43 | err = decoder.Decode(&wallets) 44 | if err != nil { 45 | 46 | log.Panic(err) 47 | } 48 | 49 | return &wallets, err 50 | } 51 | 52 | //2.创建新钱包 53 | func (wallets *Wallets) CreateWallet() { 54 | 55 | wallet := NewWallet() 56 | fmt.Printf("Your new addres:%s\n",wallet.GetAddress()) 57 | wallets.Wallets[string(wallet.GetAddress())] = wallet 58 | 59 | //保存到本地 60 | wallets.SaveWallets() 61 | } 62 | 63 | //3.保存钱包集信息到文件 64 | func (wallets *Wallets) SaveWallets() { 65 | 66 | var context bytes.Buffer 67 | 68 | //注册是为了可以序列化任何类型 69 | gob.Register(elliptic.P256()) 70 | encoder :=gob.NewEncoder(&context) 71 | err := encoder.Encode(&wallets) 72 | if err != nil { 73 | 74 | log.Panic(err) 75 | } 76 | 77 | // 将序列化以后的数覆盖写入到文件 78 | err = ioutil.WriteFile(WalletFile, context.Bytes(), 0664) 79 | if err != nil { 80 | 81 | log.Panic(err) 82 | } 83 | } -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/Wallets.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part12-MerkleTree_Prototype/Wallets.dat -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part12-MerkleTree_Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/goLang公链实战之MerkleTree.md: -------------------------------------------------------------------------------- 1 | # goLang公链实战之Merkle树 2 | 3 | # MerkleTree 4 | MerkleTree,通常也被称作Hash Tree,顾名思义,就是存储hash值的一棵树。Merkle树的叶子是数据块(例如,文件或者文件的集合)的hash值。非叶节点是其对应子节点串联字符串的hash。 5 | 6 | 下面是一幅表示MerkleTree结构的图: 7 | 8 | ![MerkleTree结构.png](https://upload-images.jianshu.io/upload_images/830585-1c6496ed8a8efd42.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 9 | 10 | 那么MerkleTree对我们构造公链有什么用呢? 11 | 12 | 我们知道完整的比特币数据库已达到一百多Gb的存储,对于每一个节点必须保存一个区块链的完整副本。这对于大多数使用比特币的人显然不合适,于是中本聪提出了简单支付验证SPV(Simplified Payment Verification). 13 | 14 | 简单地说,SPV是一个轻量级的比特币节点,它并不需要下载区块链的所有数据内容。为了实现SPV,就需要有一个方式来检查某区块是否包含某一笔交易,这就是MerkleTree能帮我们解决的问题。 15 | 16 | 一个区块的结构里只有一个哈希值,但是这个哈希值包含了所有交易的哈希值。我们将区块内所有交易哈希的值两两进行哈希得到一个新的哈希值,然后再把得到的新的哈希值两两哈希...不断进行这个过程直到最后只存在一个哈希值。这样的结构是不是很像一颗二叉树,我们将这样的二叉树就叫做MerkleTree。 17 | 18 | 比特币的MerkleTree结构图: 19 | 20 | ![Bitcoin_MerkleTree.png](https://upload-images.jianshu.io/upload_images/830585-c44d5529ec2d62ed.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 21 | 22 | 这样,我们只需要一个根哈希就可以验证一笔交易是否存在于一个区块中了,因为这个根哈希可以遍历到所有交易哈希。相当于一个Merkle 树根哈希和一个 Merkle 路径。 23 | 24 | # 废话少说撸代码 25 | 26 | ### MerkleTree结构 27 | 28 | MerkleTree只包含一个根节点,每一个默克尔树节点包含数据和左右指针。每个节点都可以连接到下个节点,并依次连接到更远的节点直到叶子节点。 29 | 30 | ``` 31 | //默克尔树 32 | type MerkleTree struct { 33 | //根节点 34 | RootNode *MerkleNode 35 | } 36 | 37 | //默克尔树节点 38 | type MerkleNode struct { 39 | //做节点 40 | Left *MerkleNode 41 | //右节点 42 | Right *MerkleNode 43 | //节点数据 44 | Data []byte 45 | } 46 | ``` 47 | 48 | ### NewMerkleNode 49 | 50 | 新建节点时首先要从叶子节点开始创建,叶子节点只有数据,对交易哈希进行哈希得到叶子节点的数值;当创建非叶子节点时,将左右子节点的数据拼接进行哈希得到新节点的数据值。 51 | 52 | ``` 53 | //新建节点 54 | func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { 55 | 56 | mNode := MerkleNode{} 57 | 58 | if left == nil && right == nil { 59 | 60 | hash := sha256.Sum256(data) 61 | mNode.Data = hash[:] 62 | } else { 63 | 64 | prevHashes := append(left.Data, right.Data...) 65 | hash := sha256.Sum256(prevHashes) 66 | mNode.Data = hash[:] 67 | } 68 | 69 | mNode.Left = left 70 | mNode.Right = right 71 | 72 | return &mNode 73 | } 74 | ``` 75 | 76 | ### NewMerkleTree 77 | 78 | 当用叶子节点去生成一颗默克尔树时,必须保证叶子节点的数量为偶数,如果不是需要复制一份最后的交易哈西值到最后拼凑成偶数个交易哈希。 79 | 80 | 叶子节点两两哈希形成新的节点,新节点继续两两哈希直到最后只有一个节点。 81 | 82 | ``` 83 | // 1 2 3 --> 1 2 3 3 84 | //新建默克尔树 85 | func NewMerkleTree(datas [][]byte) *MerkleTree { 86 | 87 | var nodes []*MerkleNode 88 | 89 | //如果是奇数,添加最后一个交易哈希拼凑为偶数个交易 90 | if len(datas) % 2 != 0 { 91 | 92 | datas = append(datas, datas[len(datas)-1]) 93 | } 94 | 95 | //将每一个交易哈希构造为默克尔树节点 96 | for _, data := range datas { 97 | 98 | node := NewMerkleNode(nil, nil, data) 99 | nodes = append(nodes, node) 100 | } 101 | 102 | //将所有节点两两组合生成新节点,直到最后只有一个更节点 103 | for i := 0; i < len(datas)/2; i++ { 104 | 105 | var newLevel []*MerkleNode 106 | 107 | for j := 0; j < len(nodes); j += 2 { 108 | 109 | node := NewMerkleNode(nodes[j], nodes[j+1], nil) 110 | newLevel = append(newLevel, node) 111 | } 112 | 113 | nodes = newLevel 114 | } 115 | 116 | //取根节点返回 117 | mTree := MerkleTree{nodes[0]} 118 | 119 | return &mTree 120 | } 121 | ``` 122 | 123 | ### 集成到Block中 124 | 125 | 在Block中使用MerkleTree优化得到一个区块交易哈希的方法: 126 | 127 | ``` 128 | 129 | // 需要将Txs转换成[]byte 130 | func (block *Block) HashTransactions() []byte { 131 | 132 | //引入MerkleTree前的交易哈希 133 | //var txHashes [][]byte 134 | //var txHash [32]byte 135 | // 136 | //for _, tx := range block.Txs { 137 | // 138 | // txHashes = append(txHashes, tx.TxHAsh) 139 | //} 140 | //txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 141 | // 142 | //return txHash[:] 143 | 144 | //默克尔树根节点表示交易哈希 145 | var transactions [][]byte 146 | 147 | for _, tx := range block.Txs { 148 | 149 | transactions = append(transactions, tx.Serialize()) 150 | } 151 | mTree := NewMerkleTree(transactions) 152 | 153 | return mTree.RootNode.Data 154 | } 155 | ``` 156 | 157 | 至此,大功告成。我们已将MerkleTree集成到项目中。当然,目前还看不出其作用。今天只是初窥MerkleTree的概念和用法,在之后的公链项目中再具体讲其对于区块链的妙用。 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part12-MerkleTree_Prototype/main -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part11-transaction_1_Prototype/BLC" 6 | ) 7 | 8 | func main() { 9 | 10 | cli := BLC.CLI{} 11 | cli.Run() 12 | 13 | //wallets := BLC.NewWallets() 14 | //address := hex.EncodeToString(wallet.GetAddress()) 15 | //wallet1 := BLC.NewWallet() 16 | //address1 := hex.EncodeToString(wallet1.GetAddress()) 17 | // 18 | //var from []string 19 | //var to []string 20 | //var amount []string 21 | //from = append(from, address) 22 | //to =append(to, address1) 23 | //amount = append(amount, "5") 24 | // 25 | //blc := BLC.CreateBlockchainWithGensisBlock(address) 26 | //blc.MineNewBlock(from, to, amount) 27 | 28 | /** 29 | //Wallet 30 | ripemd160.New() 31 | wallet := BLC.NewWallet() 32 | address := wallet.GetAddress() 33 | 34 | fmt.Printf("%s\n", address) 35 | //1CiS8axkfLGQUYaeZsuS2Fpv4nVcd6HQqk 和比特币地址相同,可以blockchaininfo查询余额 36 | //当然一定为0 37 | 38 | //判断地址有效性 39 | fmt.Println(wallet.IsValidForAddress(address)) 40 | //修改address 41 | fmt.Println(wallet.IsValidForAddress([]byte("1CiS8axkfLGQUYaeZsuS2Fpv4nVcd6HQqkk"))) 42 | 43 | //Wallets 44 | wallets := BLC.NewWallets() 45 | wallets.CreateWallet() 46 | wallets.CreateWallet() 47 | wallets.CreateWallet() 48 | fmt.Println() 49 | fmt.Println(wallets) 50 | 51 | //blc := BLC.CreateBlockchainWithGensisBlock("chaors") 52 | //utxos := blc.UnUTXOs("chaors") 53 | //fmt.Println(utxos) 54 | */ 55 | } 56 | -------------------------------------------------------------------------------- /part12-MerkleTree_Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 转账 3 | 1.reward 4 | 2.UTXOSet 查询余额的时候遍历了整个数据库,怎么更简单 ResetUTXOSet创世区块时初始化 5 | 查询余额 6 | 转账改变的UTXO 7 | FindUnPackageSpendableUTXOS Set本身就是UTXO集合,里面的不全是未花费吗???? 8 | 9 | 3.修改多笔交易验签的bug,添加txs 10 | 11 | 12 | **/ -------------------------------------------------------------------------------- /part13-Network_Prototype/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /part13-Network_Prototype/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /part13-Network_Prototype/.idea/part13-Network_Prototype.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /part13-Network_Prototype/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Base58.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | ) 7 | 8 | /** 9 | Base58是用于比特币中使用的一种独特的编码方式,主要用于产生比特币的钱包地址。 10 | 11 | 相比的Base64,Base58不使用数字 “0”,字母大写 “O”,字母大写 “I”,和字母小写 “L”,以及 “+” 和 “/” 符号。 12 | 13 | 设计Base58主要的目的是: 14 | 15 | 避免混淆。在某些字体下,数字0和字母大写O,以及字母大写我和字母小写升会非常相似。 16 | 不使用 “+” 和 “/” 的原因是非字母或数字的字符串作为帐号较难被接受。 17 | 没有标点符号,通常不会被从中间分行。 18 | 大部分的软件支持双击选择整个字符串。 19 | 但是这个base58的计算量比BASE64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。 20 | 而且长度也比的base64稍微多了一点。 21 | */ 22 | 23 | //base64:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 24 | //base58:去掉0(零),O(大写的 o),I(大写的i),l(小写的 L),+,/ 25 | 26 | //base58编码集 27 | var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 28 | 29 | // 字节数组转 Base58,加密 30 | func Base58Encode(input []byte) []byte { 31 | 32 | var result []byte 33 | 34 | x := big.NewInt(0).SetBytes(input) 35 | 36 | base := big.NewInt(int64(len(b58Alphabet))) 37 | zero := big.NewInt(0) 38 | mod := &big.Int{} 39 | 40 | for x.Cmp(zero) != 0 { 41 | 42 | x.DivMod(x, base, mod) 43 | result = append(result, b58Alphabet[mod.Int64()]) 44 | } 45 | 46 | ReverseBytes(result) 47 | for b := range input { 48 | 49 | if b == 0x00 { 50 | 51 | result = append([]byte{b58Alphabet[0]}, result...) 52 | } else { 53 | 54 | break 55 | } 56 | } 57 | 58 | return result 59 | } 60 | 61 | // Base58转字节数组,解密 62 | func Base58Decode(input []byte) []byte { 63 | 64 | result := big.NewInt(0) 65 | zeroBytes := 0 66 | 67 | for b := range input { 68 | 69 | if b == 0x00 { 70 | 71 | zeroBytes++ 72 | } 73 | } 74 | 75 | payload := input[zeroBytes:] 76 | for _, b := range payload { 77 | 78 | charIndex := bytes.IndexByte(b58Alphabet, b) 79 | result.Mul(result, big.NewInt(58)) 80 | result.Add(result, big.NewInt(int64(charIndex))) 81 | } 82 | 83 | decoded := result.Bytes() 84 | //decoded...表示将decoded所有字节追加 85 | decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...) 86 | 87 | return decoded 88 | } 89 | 90 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | ) 20 | 21 | type Block struct { 22 | //1.区块高度 23 | Height int64 24 | //2.上一个区块HAsh 25 | PrevBlockHash []byte 26 | //3.交易数据 27 | Txs [] *Transaction 28 | //4.时间戳 29 | Timestamp int64 30 | //5.Hash 31 | Hash []byte 32 | //6.Nonce 符合工作量证明的随机数 33 | Nonce int64 34 | } 35 | 36 | //区块序列化 37 | func (block *Block) Serialize() []byte { 38 | 39 | var result bytes.Buffer 40 | 41 | encoder := gob.NewEncoder(&result) 42 | 43 | err := encoder.Encode(block) 44 | if err != nil { 45 | log.Panic(err) 46 | } 47 | 48 | return result.Bytes() 49 | } 50 | 51 | //区块反序列化 52 | func DeSerializeBlock(blockBytes []byte) *Block { 53 | 54 | var block Block 55 | 56 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 57 | 58 | err := decoder.Decode(&block) 59 | 60 | if err != nil { 61 | 62 | log.Panic(err) 63 | } 64 | 65 | return &block 66 | } 67 | 68 | 69 | //1.创建新的区块 70 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 71 | 72 | //创建区块 73 | block := &Block{ 74 | Height: height, 75 | PrevBlockHash: prevBlockHash, 76 | Txs: txs, 77 | Timestamp: time.Now().Unix(), 78 | Hash: nil, 79 | Nonce: 0} 80 | 81 | //调用工作量证明返回有效的Hash 82 | pow := NewProofOfWork(block) 83 | hash, nonce := pow.Run() 84 | block.Hash = hash[:] 85 | block.Nonce = nonce 86 | 87 | fmt.Printf("\r######%d-%x\n", nonce, hash) 88 | 89 | return block 90 | } 91 | 92 | //单独方法生成创世区块 93 | func CreateGenesisBlock(txs []*Transaction) *Block { 94 | 95 | return NewBlock( 96 | txs, 97 | 1, 98 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 99 | ) 100 | } 101 | 102 | // 需要将Txs转换成[]byte 103 | func (block *Block) HashTransactions() []byte { 104 | 105 | //引入MerkleTree前的交易哈希 106 | //var TxHashes [][]byte 107 | //var TxHash [32]byte 108 | // 109 | //for _, tx := range block.Txs { 110 | // 111 | // TxHashes = append(TxHashes, tx.TxHash) 112 | //} 113 | //TxHash = sha256.Sum256(bytes.Join(TxHashes, []byte{})) 114 | // 115 | //return TxHash[:] 116 | 117 | //默克尔树根节点表示交易哈希 118 | var transactions [][]byte 119 | 120 | for _, tx := range block.Txs { 121 | 122 | transactions = append(transactions, tx.Serialize()) 123 | } 124 | mTree := NewMerkleTree(transactions) 125 | 126 | return mTree.RootNode.Data 127 | } 128 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error{ 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | 24 | if b != nil { 25 | 26 | currentBlockBytes := b.Get(blcIterator.CurrHash) 27 | 28 | if currentBlockBytes != nil { 29 | 30 | // 获取到当前迭代器里面的currentHash所对应的区块 31 | block = DeSerializeBlock(currentBlockBytes) 32 | 33 | // 更新迭代器里面CurrentHash 34 | blcIterator.CurrHash = block.PrevBlockHash 35 | } 36 | } 37 | 38 | return nil 39 | }) 40 | if err != nil { 41 | log.Panic(err) 42 | } 43 | 44 | return block 45 | } 46 | 47 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_StartNode.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func (cli *CLI) startNode(nodeID string, minerAdd string) { 9 | 10 | fmt.Printf("start Server:localhost:%s\n", nodeID) 11 | // 挖矿地址判断 12 | if len(minerAdd) > 0 { 13 | 14 | if IsValidForAddress([]byte(minerAdd)) { 15 | 16 | fmt.Printf("Miner:%s is ready to mining...\n", minerAdd) 17 | }else { 18 | 19 | fmt.Println("Server address invalid....\n") 20 | os.Exit(0) 21 | } 22 | } 23 | 24 | // 启动服务器 25 | StartServer(nodeID, minerAdd) 26 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_creatBlockchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | //新建区块链 4 | func (cli *CLI)creatBlockchain(address string, nodeID string) { 5 | 6 | blockchain := CreateBlockchainWithGensisBlock(address, nodeID) 7 | defer blockchain.DB.Close() 8 | } 9 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_createWallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI)createWallet(nodeID string) { 6 | 7 | wallets, _ := NewWallets(nodeID) 8 | wallets.CreateWallet(nodeID) 9 | 10 | fmt.Println(len(wallets.Wallets)) 11 | } 12 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_getAddrestList.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) getAddressList(nodeID string) { 6 | 7 | fmt.Println("All addresses:") 8 | 9 | wallets, _ := NewWallets(nodeID) 10 | for address, _ := range wallets.Wallets { 11 | 12 | fmt.Println(address) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_getBlance.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | 6 | //查询余额 7 | func (cli *CLI) getBlance(address string, nodeID string) { 8 | 9 | fmt.Println("地址:" + address) 10 | 11 | blockchain := GetBlockchain(nodeID) 12 | defer blockchain.DB.Close() 13 | 14 | //amount := blockchain.GetBalance(address) 引入UTXOSet前的方法 15 | 16 | utxoSet := &UTXOSet{blockchain} 17 | amount := utxoSet.GetBalance(address) 18 | 19 | fmt.Printf("%s一共有%d个Token\n", address, amount) 20 | } 21 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_printchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | //打印区块链 5 | func (cli *CLI) printchain(nodeID string) { 6 | 7 | blockchain := GetBlockchain(nodeID) 8 | defer blockchain.DB.Close() 9 | 10 | blockchain.Printchain() 11 | } 12 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_send.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | //转账 9 | func (cli *CLI) send(from []string, to []string, amount []string, nodeID string, mineNow bool) { 10 | 11 | blc := GetBlockchain(nodeID) 12 | defer blc.DB.Close() 13 | 14 | utxoSet := &UTXOSet{blc} 15 | 16 | // 由交易的第一个转账地址进行打包交易并挖矿 17 | if mineNow { 18 | 19 | blc.MineNewBlock(from, to, amount, nodeID) 20 | 21 | // 转账成功以后,需要更新UTXOSet 22 | utxoSet.Update() 23 | }else { 24 | 25 | // 把交易发送到矿工节点去进行验证 26 | fmt.Println("miner deal with the Tx...") 27 | 28 | // 遍历每一笔转账构造交易 29 | var txs []*Transaction 30 | nodeAddress = fmt.Sprintf("localhost:%s", nodeID) 31 | for index, address := range from { 32 | 33 | value, _ := strconv.Atoi(amount[index]) 34 | tx := NewTransaction(address, to[index], int64(value), utxoSet, txs, nodeID) 35 | txs = append(txs, tx) 36 | 37 | // 将交易发送给主节点 38 | sendTx(knowedNodes[0], tx) 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/CLI_testMethod.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | func (cli *CLI) ResetUTXOSet(nodeID string) { 6 | 7 | 8 | fmt.Println("TestMethod") 9 | 10 | blockchain := GetBlockchain(nodeID) 11 | defer blockchain.DB.Close() 12 | 13 | utxoSet := &UTXOSet{blockchain} 14 | utxoSet.ResetUTXOSet() 15 | 16 | fmt.Println(blockchain.FindUTXOMap()) 17 | } 18 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Constant.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | const PROTOCOL = "tcp" 4 | //发送消息的前12个字节指定了命令名(version) 5 | const COMMANDLENGTH = 12 6 | const NODE_VERSION = 1 7 | 8 | // 命令 9 | const COMMAND_VERSION = "version" 10 | const COMMAND_ADDR = "addr" 11 | const COMMAND_BLOCK = "block" 12 | const COMMAND_INV = "inv" 13 | const COMMAND_GETBLOCKS = "getblocks" 14 | const COMMAND_GETDATA = "getdata" 15 | const COMMAND_TX = "tx" 16 | 17 | // 类型 18 | const BLOCK_TYPE = "block" 19 | const TX_TYPE = "tx" 20 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/MerkleTree.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "crypto/sha256" 4 | 5 | //默克尔树 6 | type MerkleTree struct { 7 | //根节点 8 | RootNode *MerkleNode 9 | } 10 | 11 | //默克尔树节点 12 | type MerkleNode struct { 13 | //做节点 14 | Left *MerkleNode 15 | //右节点 16 | Right *MerkleNode 17 | //节点数据 18 | Data []byte 19 | } 20 | 21 | //新建节点 22 | func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { 23 | 24 | mNode := MerkleNode{} 25 | 26 | if left == nil && right == nil { 27 | 28 | hash := sha256.Sum256(data) 29 | mNode.Data = hash[:] 30 | } else { 31 | 32 | prevHashes := append(left.Data, right.Data...) 33 | hash := sha256.Sum256(prevHashes) 34 | mNode.Data = hash[:] 35 | } 36 | 37 | mNode.Left = left 38 | mNode.Right = right 39 | 40 | return &mNode 41 | } 42 | 43 | // 1 2 3 --> 1 2 3 3 44 | //新建默克尔树 45 | func NewMerkleTree(datas [][]byte) *MerkleTree { 46 | 47 | var nodes []*MerkleNode 48 | 49 | //如果是奇数,添加最后一个交易哈希拼凑为偶数个交易 50 | if len(datas) % 2 != 0 { 51 | 52 | datas = append(datas, datas[len(datas)-1]) 53 | } 54 | 55 | //将每一个交易哈希构造为默克尔树节点 56 | for _, data := range datas { 57 | 58 | node := NewMerkleNode(nil, nil, data) 59 | nodes = append(nodes, node) 60 | } 61 | 62 | //将所有节点两两组合生成新节点,直到最后只有一个更节点 63 | for i := 0; i < len(datas)/2; i++ { 64 | 65 | var newLevel []*MerkleNode 66 | 67 | for j := 0; j < len(nodes); j += 2 { 68 | 69 | node := NewMerkleNode(nodes[j], nodes[j+1], nil) 70 | newLevel = append(newLevel, node) 71 | } 72 | 73 | nodes = newLevel 74 | } 75 | 76 | //取根节点返回 77 | mTree := MerkleTree{nodes[0]} 78 | 79 | return &mTree 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "net" 5 | "fmt" 6 | "log" 7 | "io/ioutil" 8 | ) 9 | 10 | 11 | func StartServer(nodeID string, minerAdd string) { 12 | 13 | // 当前节点IP地址 14 | nodeAddress = fmt.Sprintf("localhost:%s", nodeID) 15 | // 挖矿节点设置 16 | if len(minerAdd) > 0 { 17 | 18 | miningAddress = minerAdd 19 | } 20 | 21 | // 启动网络监听服务 22 | ln, err := net.Listen(PROTOCOL, nodeAddress) 23 | if err != nil { 24 | 25 | log.Panic(err) 26 | } 27 | defer ln.Close() 28 | 29 | blc := GetBlockchain(nodeID) 30 | //fmt.Println("startserver\n") 31 | //blc.Printchain() 32 | 33 | // 第一个终端:端口为3000,启动的就是主节点 34 | // 第二个终端:端口为3001,钱包节点 35 | // 第三个终端:端口号为3002,矿工节点 36 | if nodeAddress != knowedNodes[0] { 37 | 38 | // 该节点不是主节点,钱包节点向主节点请求数据 39 | sendVersion(knowedNodes[0], blc) 40 | } 41 | 42 | for { 43 | 44 | // 接收客户端发来的数据 45 | connc, err := ln.Accept() 46 | if err != nil { 47 | 48 | log.Panic(err) 49 | } 50 | 51 | // 不同的命令采取不同的处理方式 52 | go handleConnection(connc, blc) 53 | } 54 | } 55 | 56 | // 客户端命令处理器 57 | func handleConnection(conn net.Conn, blc *Blockchain) { 58 | 59 | //fmt.Println("handleConnection:\n") 60 | //blc.Printchain() 61 | 62 | // 读取客户端发送过来的所有的数据 63 | request, err := ioutil.ReadAll(conn) 64 | if err != nil { 65 | 66 | log.Panic(err) 67 | } 68 | 69 | fmt.Printf("\nReceive a Message:%s\n", request[:COMMANDLENGTH]) 70 | 71 | command := bytesToCommand(request[:COMMANDLENGTH]) 72 | 73 | switch command { 74 | 75 | case COMMAND_VERSION: 76 | handleVersion(request, blc) 77 | 78 | case COMMAND_ADDR: 79 | handleAddr(request, blc) 80 | 81 | case COMMAND_BLOCK: 82 | handleBlock(request, blc) 83 | 84 | case COMMAND_GETBLOCKS: 85 | handleGetblocks(request, blc) 86 | 87 | case COMMAND_GETDATA: 88 | handleGetData(request, blc) 89 | 90 | case COMMAND_INV: 91 | handleInv(request, blc) 92 | 93 | case COMMAND_TX: 94 | handleTx(request, blc) 95 | 96 | default: 97 | fmt.Println("Unknown command!") 98 | } 99 | 100 | defer conn.Close() 101 | } 102 | 103 | // 节点是否在已知节点中 104 | func nodeIsKnown(addr string) bool { 105 | 106 | for _, node := range knowedNodes { 107 | 108 | if node == addr { 109 | 110 | return true 111 | } 112 | } 113 | 114 | return false 115 | } 116 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_BlockData.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | // 用于节点间发送一个区块 4 | type BlockData struct { 5 | // 节点地址 6 | AddrFrom string 7 | // 序列化区块 8 | BlockBytes []byte 9 | } 10 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_GetBlocks.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | // 表示向节点请求一个块哈希的表,该请求会返回所有块的哈希 4 | type GetBlocks struct { 5 | //请求节点地址 6 | AddrFrom string 7 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_GetData.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | // 用于请求区块或交易 4 | type GetData struct { 5 | // 节点地址 6 | AddrFrom string 7 | // 请求类型 是block还是tx 8 | Type string 9 | // 区块哈希或交易哈希 10 | Hash []byte 11 | } 12 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_Inv.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | // 向其他节点展示自己拥有的区块和交易 4 | type Inv struct { 5 | // 自己的地址 6 | AddrFrom string 7 | // 类型 block tx 8 | Type string 9 | // hash二维数组 10 | Items [][]byte 11 | } 12 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_TxData.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | // 同步中传递的交易类型 4 | type TxData struct { 5 | // 节点地址 6 | AddFrom string 7 | // 交易 8 | TransactionBytes []byte 9 | } 10 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_Version.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type Version struct { 4 | // 版本 5 | Version int64 6 | // 当前节点区块的高度 7 | BestHeight int64 8 | //当前节点的地址 9 | AddrFrom string 10 | } 11 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_send.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "bytes" 7 | "log" 8 | "net" 9 | ) 10 | 11 | //COMMAND_VERSION 12 | func sendVersion(toAddress string, blc *Blockchain) { 13 | 14 | 15 | bestHeight := blc.GetBestHeight() 16 | payload := gobEncode(Version{NODE_VERSION, bestHeight, nodeAddress}) 17 | 18 | request := append(commandToBytes(COMMAND_VERSION), payload...) 19 | 20 | sendData(toAddress, request) 21 | } 22 | 23 | 24 | 25 | //COMMAND_GETBLOCKS 26 | func sendGetBlocks(toAddress string) { 27 | 28 | payload := gobEncode(GetBlocks{nodeAddress}) 29 | 30 | request := append(commandToBytes(COMMAND_GETBLOCKS), payload...) 31 | 32 | sendData(toAddress, request) 33 | 34 | } 35 | 36 | // 主节点将自己的所有的区块hash发送给钱包节点 37 | //COMMAND_BLOCK 38 | // 39 | func sendInv(toAddress string, kind string, hashes [][]byte) { 40 | 41 | payload := gobEncode(Inv{nodeAddress,kind,hashes}) 42 | 43 | request := append(commandToBytes(COMMAND_INV), payload...) 44 | 45 | sendData(toAddress, request) 46 | 47 | } 48 | 49 | 50 | 51 | func sendGetData(toAddress string, kind string ,blockHash []byte) { 52 | 53 | payload := gobEncode(GetData{nodeAddress,kind,blockHash}) 54 | 55 | request := append(commandToBytes(COMMAND_GETDATA), payload...) 56 | 57 | sendData(toAddress, request) 58 | } 59 | 60 | 61 | func sendBlock(toAddress string, blockBytes []byte) { 62 | 63 | 64 | payload := gobEncode(BlockData{nodeAddress,blockBytes}) 65 | 66 | request := append(commandToBytes(COMMAND_BLOCK), payload...) 67 | 68 | sendData(toAddress, request) 69 | } 70 | 71 | func sendTx(toAddress string, tx *Transaction) { 72 | 73 | data := TxData{nodeAddress, tx.Serialize()} 74 | payload := gobEncode(data) 75 | request := append(commandToBytes(COMMAND_TX), payload...) 76 | 77 | sendData(toAddress, request) 78 | } 79 | 80 | // 客户端向服务器发送数据 81 | func sendData(to string, data []byte) { 82 | 83 | fmt.Printf("Client send message to server:%s...\n", to) 84 | 85 | conn, err := net.Dial("tcp", to) 86 | if err != nil { 87 | 88 | panic("error") 89 | } 90 | defer conn.Close() 91 | 92 | // 要发送的数据 93 | _, err = io.Copy(conn, bytes.NewReader(data)) 94 | if err != nil { 95 | 96 | log.Panic(err) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Server_var.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | //localhost:3000 主节点的地址 5 | var knowedNodes = []string{"localhost:8000"} 6 | var nodeAddress string //全局变量,节点地址 7 | // 存储拥有最新链的未处理的区块hash值 8 | var unslovedHashes [][]byte 9 | // 交易内存池 10 | var memTxPool = make(map[string]Transaction) 11 | // 矿工地址 12 | var miningAddress string 13 | // 挖矿需要满足的最小交易数 14 | const minMinerTxCount = 1 -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "bytes" 4 | 5 | type TXInput struct { 6 | //交易ID 7 | TxHash []byte 8 | //存储TXOutput在Vouts里的索引 9 | Vout int 10 | //数字签名 11 | Signature []byte 12 | //公钥 13 | PublicKey []byte 14 | } 15 | 16 | //验证当前输入是否是当前地址的 17 | func (txInput *TXInput) UnlockWithAddress(address string) bool { 18 | 19 | //base58解码 20 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 21 | //去除版本得到地反编码的公钥两次哈希后的值 22 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 23 | 24 | //Ripemd160Hash算法得到公钥两次哈希后的值 25 | ripemd160HashNew := Ripemd160Hash(txInput.PublicKey) 26 | 27 | return bytes.Compare(ripemd160HashNew, ripemd160Hash) == 0 28 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | 5 | "bytes" 6 | ) 7 | 8 | type TXOutput struct { 9 | //面值 10 | Value int64 11 | //用户名 12 | Ripemd160Hash []byte //用户名 公钥两次哈希后的值 13 | } 14 | 15 | func NewTXOutput(value int64,address string) *TXOutput { 16 | 17 | txOutput := &TXOutput{value,nil} 18 | 19 | // 设置Ripemd160Hash 20 | txOutput.Lock(address) 21 | 22 | return txOutput 23 | } 24 | 25 | //锁定 26 | func (txOutput *TXOutput) Lock(address string) { 27 | 28 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 29 | txOutput.Ripemd160Hash = version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes)-4] 30 | } 31 | 32 | //解锁 33 | func (txOutput *TXOutput) UnLockScriptPubKeyWithAddress(address string) bool { 34 | 35 | version_pubKeyHash_checkSumBytes := Base58Decode([]byte(address)) 36 | ripemd160Hash := version_pubKeyHash_checkSumBytes[1:len(version_pubKeyHash_checkSumBytes) - 4] 37 | 38 | //fmt.Println(txOutput.Ripemd160Hash, ripemd160Hash) 39 | return bytes.Compare(txOutput.Ripemd160Hash, ripemd160Hash) == 0 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/TXOutputs.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "log" 6 | "bytes" 7 | ) 8 | 9 | type TXOutputs struct { 10 | 11 | UTXOS []*UTXO 12 | } 13 | 14 | 15 | // 序列化成字节数组 16 | func (txOutputs *TXOutputs) Serialize() []byte { 17 | 18 | var result bytes.Buffer 19 | 20 | encoder := gob.NewEncoder(&result) 21 | 22 | err := encoder.Encode(txOutputs) 23 | if err != nil { 24 | log.Panic(err) 25 | } 26 | 27 | return result.Bytes() 28 | } 29 | 30 | // 反序列化 31 | func DeserializeTXOutputs(txOutputsBytes []byte) *TXOutputs { 32 | 33 | var txOutputs TXOutputs 34 | 35 | decoder := gob.NewDecoder(bytes.NewReader(txOutputsBytes)) 36 | err := decoder.Decode(&txOutputs) 37 | if err != nil { 38 | 39 | log.Panic(err) 40 | } 41 | 42 | return &txOutputs 43 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/UTXO.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type UTXO struct { 4 | //来自交易的哈希 5 | TxHash []byte 6 | //在该交易VOuts里的下标 7 | Index int 8 | //未花费的交易输出 9 | Output *TXOutput 10 | } 11 | 12 | -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | "encoding/json" 19 | "encoding/gob" 20 | "fmt" 21 | ) 22 | 23 | //将int64转换为bytes 24 | func IntToHex(num int64) []byte { 25 | 26 | buff := new(bytes.Buffer) 27 | err := binary.Write(buff, binary.BigEndian, num) 28 | if err != nil { 29 | 30 | log.Panic(err) 31 | } 32 | 33 | return buff.Bytes() 34 | } 35 | 36 | // 标准的JSON字符串转数组 37 | func Json2Array(jsonString string) []string { 38 | 39 | //json 到 []string 40 | var sArr []string 41 | if err := json.Unmarshal([]byte(jsonString), &sArr); err != nil { 42 | 43 | log.Panic(err) 44 | } 45 | return sArr 46 | } 47 | 48 | 49 | // 字节数组反转 50 | func ReverseBytes(data []byte) { 51 | 52 | for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 { 53 | 54 | data[i], data[j] = data[j], data[i] 55 | } 56 | } 57 | 58 | 59 | // 将结构体序列化成字节数组 60 | func gobEncode(data interface{}) []byte { 61 | 62 | var buff bytes.Buffer 63 | 64 | enc := gob.NewEncoder(&buff) 65 | err := enc.Encode(data) 66 | if err != nil { 67 | log.Panic(err) 68 | } 69 | 70 | return buff.Bytes() 71 | } 72 | 73 | func commandToBytes(command string) []byte { 74 | 75 | // 消息在底层就是字节序列,前12个字节指定了命令名(比如这里的 version) 76 | var bytes [COMMANDLENGTH]byte 77 | 78 | for i, c := range command { 79 | bytes[i] = byte(c) 80 | } 81 | 82 | return bytes[:] 83 | } 84 | 85 | func bytesToCommand(bytes []byte) string { 86 | var command []byte 87 | 88 | for _, b := range bytes { 89 | if b != 0x0 { 90 | command = append(command, b) 91 | } 92 | } 93 | 94 | return fmt.Sprintf("%s", command) 95 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Wallet.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "log" 8 | "crypto/sha256" 9 | "golang.org/x/crypto/ripemd160" 10 | "bytes" 11 | ) 12 | 13 | //用于生成地址的版本 14 | const AddVersion = byte(0x00) 15 | //用于生成地址的校验和位数 16 | const AddressChecksumLen = 4 17 | 18 | 19 | type Wallet struct { 20 | //私钥 21 | PrivateKey ecdsa.PrivateKey 22 | //公钥 23 | PublicKey []byte 24 | } 25 | 26 | //1.创建钱包 27 | func NewWallet () *Wallet { 28 | 29 | privateKey, publicKey := newKeyPair() 30 | 31 | return &Wallet{privateKey, publicKey} 32 | } 33 | 34 | //通过私钥创建公钥 35 | func newKeyPair() (ecdsa.PrivateKey, []byte) { 36 | 37 | //1.椭圆曲线算法生成私钥 38 | curve := elliptic.P256() 39 | privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) 40 | if err != nil { 41 | 42 | log.Panic(err) 43 | } 44 | 45 | //2.通过私钥生成公钥 46 | publicKey := append(privateKey.PublicKey.X.Bytes(), privateKey.PublicKey.Y.Bytes()...) 47 | 48 | 49 | return *privateKey, publicKey 50 | } 51 | 52 | //2.获取钱包地址 根据公钥生成地址 53 | func (wallet *Wallet) GetAddress() []byte { 54 | 55 | //1.使用RIPEMD160(SHA256(PubKey)) 哈希算法,取公钥并对其哈希两次 56 | ripemd160Hash := Ripemd160Hash(wallet.PublicKey) 57 | //2.拼接版本 58 | version_ripemd160Hash := append([]byte{AddVersion}, ripemd160Hash...) 59 | //3.两次sha256生成校验和 60 | checkSumBytes := CheckSum(version_ripemd160Hash) 61 | //4.拼接校验和 62 | bytes := append(version_ripemd160Hash, checkSumBytes...) 63 | 64 | //5.base58编码 65 | return Base58Encode(bytes) 66 | } 67 | 68 | //将公钥进行两次哈希 69 | func Ripemd160Hash(publicKey []byte) []byte { 70 | 71 | //1.hash256 72 | hash256 := sha256.New() 73 | hash256.Write(publicKey) 74 | hash := hash256.Sum(nil) 75 | 76 | //2.ripemd160 77 | ripemd160 := ripemd160.New() 78 | ripemd160.Write(hash) 79 | 80 | return ripemd160.Sum(nil) 81 | } 82 | 83 | //两次sha256哈希生成校验和 84 | func CheckSum(bytes []byte) []byte { 85 | 86 | //hasher := sha256.New() 87 | //hasher.Write(bytes) 88 | //hash := hasher.Sum(nil) 89 | //与下面一句等同 90 | //hash := sha256.Sum256(bytes) 91 | 92 | hash1 := sha256.Sum256(bytes) 93 | hash2 := sha256.Sum256(hash1[:]) 94 | 95 | return hash2[:AddressChecksumLen] 96 | } 97 | 98 | //3.判断地址是否有效 99 | func IsValidForAddress(address []byte) bool { 100 | 101 | //1.base58解码地址得到版本,公钥哈希和校验位拼接的字节数组 102 | version_publicKey_checksumBytes := Base58Decode(address) 103 | //2.获取校验位和version_publicKeHash 104 | checkSumBytes := version_publicKey_checksumBytes[len(version_publicKey_checksumBytes)-AddressChecksumLen:] 105 | version_ripemd160 := version_publicKey_checksumBytes[:len(version_publicKey_checksumBytes)-AddressChecksumLen] 106 | 107 | //3.重新用解码后的version_ripemd160获得校验和 108 | checkSumBytesNew := CheckSum(version_ripemd160) 109 | 110 | //4.比较解码生成的校验和CheckSum重新计算的校验和 111 | if bytes.Compare(checkSumBytes, checkSumBytesNew) == 0 { 112 | 113 | return true 114 | } 115 | 116 | return false 117 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/BLC/Wallets.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "encoding/gob" 5 | "crypto/elliptic" 6 | "bytes" 7 | "log" 8 | "io/ioutil" 9 | "os" 10 | "fmt" 11 | ) 12 | 13 | //存储钱包集的文件名 14 | const WalletFile = "Wallets_%s.dat" 15 | 16 | type Wallets struct { 17 | Wallets map[string] *Wallet 18 | } 19 | 20 | //1.创建钱包集合 21 | func NewWallets(nodeID string) (*Wallets, error) { 22 | 23 | //拼接钱包文件名,表示该钱包属于哪一个节点 24 | WalletFile := fmt.Sprintf(WalletFile, nodeID) 25 | 26 | //判断文件是否存在 27 | if _, err := os.Stat(WalletFile); os.IsNotExist(err) { 28 | 29 | wallets := &Wallets{} 30 | wallets.Wallets = make(map[string] *Wallet) 31 | 32 | return wallets, err 33 | } 34 | 35 | 36 | var wallets Wallets 37 | //读取文件 38 | fileContent, err := ioutil.ReadFile(WalletFile) 39 | if err != nil { 40 | 41 | log.Panic(err) 42 | } 43 | 44 | gob.Register(elliptic.P256()) 45 | decoder := gob.NewDecoder(bytes.NewReader(fileContent)) 46 | err = decoder.Decode(&wallets) 47 | if err != nil { 48 | 49 | log.Panic(err) 50 | } 51 | 52 | return &wallets, err 53 | } 54 | 55 | //2.创建新钱包 56 | func (wallets *Wallets) CreateWallet(nodeID string) { 57 | 58 | wallet := NewWallet() 59 | fmt.Printf("Your new addres:%s\n",wallet.GetAddress()) 60 | wallets.Wallets[string(wallet.GetAddress())] = wallet 61 | 62 | //保存到本地 63 | wallets.SaveWallets(nodeID) 64 | } 65 | 66 | //3.保存钱包集信息到文件 67 | func (wallets *Wallets) SaveWallets(nodeID string) { 68 | 69 | WalletFile := fmt.Sprintf(WalletFile, nodeID) 70 | 71 | var context bytes.Buffer 72 | 73 | //注册是为了可以序列化任何类型 74 | gob.Register(elliptic.P256()) 75 | encoder :=gob.NewEncoder(&context) 76 | err := encoder.Encode(&wallets) 77 | if err != nil { 78 | 79 | log.Panic(err) 80 | } 81 | 82 | // 将序列化以后的数覆盖写入到文件 83 | err = ioutil.WriteFile(WalletFile, context.Bytes(), 0664) 84 | if err != nil { 85 | 86 | log.Panic(err) 87 | } 88 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part13-Network_Prototype/BLC" 6 | ) 7 | 8 | func main() { 9 | 10 | cli := BLC.CLI{} 11 | cli.Run() 12 | } -------------------------------------------------------------------------------- /part13-Network_Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 转账 3 | 1.cli_NODE_ID createGensisBlock 4 | 2.tcp tstartnode 5 | 3.version 6 | 7 | 8 | **/ -------------------------------------------------------------------------------- /part2-ProofOfWork-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "fmt" 16 | ) 17 | 18 | type Block struct { 19 | //1.区块高度 20 | Height int64 21 | //2.上一个区块HAsh 22 | PrevBlockHash []byte 23 | //3.交易数据 24 | Data []byte 25 | //4.时间戳 26 | Timestamp int64 27 | //5.Hash 28 | Hash []byte 29 | //6.Nonce 符合工作量证明的随机数 30 | Nonce int64 31 | } 32 | 33 | //1.创建新的区块 34 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 35 | 36 | //创建区块 37 | block := &Block{ 38 | Height: height, 39 | PrevBlockHash: prevBlockHash, 40 | Data: []byte(data), 41 | Timestamp: time.Now().Unix(), 42 | Hash: nil, 43 | Nonce: 0} 44 | 45 | //调用工作量证明返回有效的Hash 46 | pow := NewProofOfWork(block) 47 | hash, nonce := pow.Run() 48 | block.Hash = hash[:] 49 | block.Nonce = nonce 50 | 51 | fmt.Printf("\r%d-%x\n", nonce, hash) 52 | 53 | return block 54 | } 55 | 56 | //单独方法生成创世区块 57 | func CreateGenesisBlock(data string) *Block { 58 | 59 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 60 | } 61 | -------------------------------------------------------------------------------- /part2-ProofOfWork-Prototype/BLC/Blockchain.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Blockchain.go 5 | 6 | @time: 2018/06/21 22:40 7 | 8 | @desc: 区块链基础结构 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | type Blockchain struct { 15 | //有序区块的数组 16 | Blocks [] *Block 17 | } 18 | 19 | //1.创建带有创世区块的区块链 20 | func CreateBlockchainWithGensisBlock() *Blockchain { 21 | 22 | gensisBlock := CreateGenesisBlock("Gensis Block...") 23 | 24 | return &Blockchain{[] *Block{gensisBlock}} 25 | } 26 | 27 | //2.新增一个区块到区块链 28 | func (blc *Blockchain) AddBlockToBlockchain(data string, height int64, prevHash []byte) { 29 | 30 | //新建区块 31 | newBlock := NewBlock(data, height, prevHash) 32 | //上链 33 | blc.Blocks = append(blc.Blocks, newBlock) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /part2-ProofOfWork-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | ) 8 | 9 | //期望计算的Hash值前面至少要有16个零 10 | const targetBits = 16 11 | 12 | type ProofOfWork struct { 13 | //求工作量的block 14 | Block *Block 15 | //工作量难度 big.Int大数存储 16 | target *big.Int 17 | } 18 | 19 | //创建新的工作量证明对象 20 | func NewProofOfWork(block *Block) *ProofOfWork { 21 | 22 | /** 23 | target计算方式 假设:Hash为8位,targetBit为2位 24 | eg:0000 0001(8位的Hash) 25 | 1.8-2 = 6 将上值左移6位 26 | 2.0000 0001 << 6 = 0100 0000 = target 27 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 28 | */ 29 | 30 | //1.创建一个初始值为1的target 31 | target := big.NewInt(1) 32 | //2.左移bits(Hash) - targetBit 位 33 | target = target.Lsh(target, 256-targetBits) 34 | 35 | return &ProofOfWork{block, target} 36 | } 37 | 38 | //拼接区块属性,返回字节数组 39 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 40 | 41 | data := bytes.Join( 42 | [][]byte{ 43 | pow.Block.PrevBlockHash, 44 | pow.Block.Data, 45 | IntToHex(pow.Block.Timestamp), 46 | IntToHex(int64(targetBits)), 47 | IntToHex(int64(nonce)), 48 | IntToHex(int64(pow.Block.Height)), 49 | }, 50 | []byte{}, 51 | ) 52 | 53 | return data 54 | } 55 | 56 | //判断当前区块是否有效 57 | func (proofOfWork *ProofOfWork) IsValid() bool { 58 | 59 | //比较当前区块哈希值与目标哈希值 60 | var hashInt big.Int 61 | hashInt.SetBytes(proofOfWork.Block.Hash) 62 | 63 | if proofOfWork.target.Cmp(&hashInt) == 1 { 64 | 65 | return true 66 | } 67 | 68 | return false 69 | } 70 | 71 | //运行工作量证明 72 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 73 | 74 | //1.将Block属性拼接成字节数组 75 | 76 | //2.生成hash 77 | //3.判断Hash值有效性,如果满足条件跳出循环 78 | 79 | //用于寻找目标hash值的随机数 80 | nonce := 0 81 | //存储新生成的Hash值 82 | var hashInt big.Int 83 | var hash [32]byte 84 | 85 | for { 86 | //准备数据 87 | dataBytes := proofOfWork.prepareData(nonce) 88 | //生成Hash 89 | hash = sha256.Sum256(dataBytes) 90 | 91 | //\r将当前打印行覆盖 92 | //fmt.Printf("\r%x", hash) 93 | //存储Hash到hashInt 94 | hashInt.SetBytes(hash[:]) 95 | //验证Hash 96 | if proofOfWork.target.Cmp(&hashInt) == 1 { 97 | 98 | break 99 | } 100 | nonce++ 101 | } 102 | 103 | return hash[:], int64(nonce) 104 | } 105 | 106 | 107 | -------------------------------------------------------------------------------- /part2-ProofOfWork-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part2-ProofOfWork-Prototype/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: main.go 5 | 6 | @time: 2018/06/21 22:01 7 | 8 | @desc: 区块信息的示例 9 | */ 10 | 11 | 12 | package main 13 | 14 | import ( 15 | "chaors.com/LearnGo/publicChaorsChain/part2-ProofOfWork-Prototype/BLC" 16 | "fmt" 17 | ) 18 | 19 | func main() { 20 | 21 | //genesisBlock := BLC.CreateGenesisBlock("Genenis Block") 22 | //创建带有创世区块的区块链 23 | blockchain := BLC.CreateBlockchainWithGensisBlock() 24 | //添加一个新区快 25 | blockchain.AddBlockToBlockchain("first Block", 26 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 27 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 28 | blockchain.AddBlockToBlockchain("second Block", 29 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 30 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 31 | blockchain.AddBlockToBlockchain("third Block", 32 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 33 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 34 | fmt.Println(blockchain) 35 | } 36 | -------------------------------------------------------------------------------- /part2-ProofOfWork-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 工作量证明 3 | **/ -------------------------------------------------------------------------------- /part3-boltdb-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | ) 19 | 20 | type Block struct { 21 | //1.区块高度 22 | Height int64 23 | //2.上一个区块HAsh 24 | PrevBlockHash []byte 25 | //3.交易数据 26 | Data []byte 27 | //4.时间戳 28 | Timestamp int64 29 | //5.Hash 30 | Hash []byte 31 | //6.Nonce 符合工作量证明的随机数 32 | Nonce int64 33 | } 34 | 35 | //区块序列化 36 | func (block *Block) Serialize() []byte { 37 | 38 | var result bytes.Buffer 39 | encoder := gob.NewEncoder(&result) 40 | 41 | err := encoder.Encode(block) 42 | if err != nil{ 43 | 44 | log.Panic(err) 45 | } 46 | 47 | return result.Bytes() 48 | } 49 | 50 | //区块序列化 51 | func DeSerializeBlock(blockBytes []byte) *Block { 52 | 53 | var block Block 54 | 55 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 56 | 57 | err := decoder.Decode(&block) 58 | if err != nil { 59 | 60 | log.Panic(err) 61 | } 62 | 63 | return &block 64 | } 65 | 66 | 67 | //1.创建新的区块 68 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 69 | 70 | //创建区块 71 | block := &Block{ 72 | Height: height, 73 | PrevBlockHash: prevBlockHash, 74 | Data: []byte(data), 75 | Timestamp: time.Now().Unix(), 76 | Hash: nil, 77 | Nonce: 0} 78 | 79 | //调用工作量证明返回有效的Hash 80 | pow := NewProofOfWork(block) 81 | hash, nonce := pow.Run() 82 | block.Hash = hash[:] 83 | block.Nonce = nonce 84 | 85 | //fmt.Printf("\r%d-%x\n", nonce, hash) 86 | 87 | return block 88 | } 89 | 90 | //单独方法生成创世区块 91 | func CreateGenesisBlock(data string) *Block { 92 | 93 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 94 | } 95 | -------------------------------------------------------------------------------- /part3-boltdb-Prototype/BLC/Blockchain.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Blockchain.go 5 | 6 | @time: 2018/06/21 22:40 7 | 8 | @desc: 区块链基础结构 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | type Blockchain struct { 15 | //有序区块的数组 16 | Blocks [] *Block 17 | } 18 | 19 | //1.创建带有创世区块的区块链 20 | func CreateBlockchainWithGensisBlock() *Blockchain { 21 | 22 | gensisBlock := CreateGenesisBlock("Gensis Block...") 23 | 24 | return &Blockchain{[] *Block{gensisBlock}} 25 | } 26 | 27 | //2.新增一个区块到区块链 28 | func (blc *Blockchain) AddBlockToBlockchain(data string, height int64, prevHash []byte) { 29 | 30 | //新建区块 31 | newBlock := NewBlock(data, height, prevHash) 32 | //上链 33 | blc.Blocks = append(blc.Blocks, newBlock) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /part3-boltdb-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | ) 8 | 9 | //期望计算的Hash值前面至少要有16个零 10 | const targetBits = 16 11 | 12 | type ProofOfWork struct { 13 | //求工作量的block 14 | Block *Block 15 | //工作量难度 big.Int大数存储 16 | target *big.Int 17 | } 18 | 19 | //创建新的工作量证明对象 20 | func NewProofOfWork(block *Block) *ProofOfWork { 21 | 22 | /** 23 | target计算方式 假设:Hash为8位,targetBit为2位 24 | eg:0000 0001(8位的Hash) 25 | 1.8-2 = 6 将上值左移6位 26 | 2.0000 0001 << 6 = 0100 0000 = target 27 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 28 | */ 29 | 30 | //1.创建一个初始值为1的target 31 | target := big.NewInt(1) 32 | //2.左移bits(Hash) - targetBit 位 33 | target = target.Lsh(target, 256-targetBits) 34 | 35 | return &ProofOfWork{block, target} 36 | } 37 | 38 | //拼接区块属性,返回字节数组 39 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 40 | 41 | data := bytes.Join( 42 | [][]byte{ 43 | pow.Block.PrevBlockHash, 44 | pow.Block.Data, 45 | IntToHex(pow.Block.Timestamp), 46 | IntToHex(int64(targetBits)), 47 | IntToHex(int64(nonce)), 48 | IntToHex(int64(pow.Block.Height)), 49 | }, 50 | []byte{}, 51 | ) 52 | 53 | return data 54 | } 55 | 56 | //判断当前区块是否有效 57 | func (proofOfWork *ProofOfWork) IsValid() bool { 58 | 59 | //比较当前区块哈希值与目标哈希值 60 | var hashInt big.Int 61 | hashInt.SetBytes(proofOfWork.Block.Hash) 62 | 63 | if proofOfWork.target.Cmp(&hashInt) == 1 { 64 | 65 | return true 66 | } 67 | 68 | return false 69 | } 70 | 71 | //运行工作量证明 72 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 73 | 74 | //1.将Block属性拼接成字节数组 75 | 76 | //2.生成hash 77 | //3.判断Hash值有效性,如果满足条件跳出循环 78 | 79 | //用于寻找目标hash值的随机数 80 | nonce := 0 81 | //存储新生成的Hash值 82 | var hashInt big.Int 83 | var hash [32]byte 84 | 85 | for { 86 | //准备数据 87 | dataBytes := proofOfWork.prepareData(nonce) 88 | //生成Hash 89 | hash = sha256.Sum256(dataBytes) 90 | 91 | //\r将当前打印行覆盖 92 | //fmt.Printf("\r%x", hash) 93 | //存储Hash到hashInt 94 | hashInt.SetBytes(hash[:]) 95 | //验证Hash 96 | if proofOfWork.target.Cmp(&hashInt) == 1 { 97 | 98 | break 99 | } 100 | nonce++ 101 | } 102 | 103 | return hash[:], int64(nonce) 104 | } 105 | 106 | 107 | -------------------------------------------------------------------------------- /part3-boltdb-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part3-boltdb-Prototype/chaorsBlock.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part3-boltdb-Prototype/chaorsBlock.db -------------------------------------------------------------------------------- /part3-boltdb-Prototype/goLang公链实战之boltdb数据库.md: -------------------------------------------------------------------------------- 1 | # goLang公链实战之boltdb数据库 2 | 3 | 我们知道,bitcoin客户端的区块信息是存储在LevelDB数据库中。我们既然要基于go开发公链,这里用到的数据库是基于go的[boltDB](https://github.com/boltdb)。 4 | 5 | ### 安装 6 | 7 | 使用go get 8 | ``` 9 | $ go get github.com/boltdb/boltd / ... 10 | ``` 11 | 12 | 安装成功后,我们会在go目录下看到: 13 | 14 | ![boltdb安装目录](https://upload-images.jianshu.io/upload_images/830585-883a42d48e5e3518.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 15 | 16 | ### 基本使用 17 | 18 | ##### 创建并打开数据库 19 | 注意:在这里gland直接运行,生成的my.db在main.go上层目录;命令行build在运行的话是当前目录!!! 20 | ``` 21 | //1.数据库创建 22 | //在这里gland直接运行,生成的my.db在main.go上层目录;命令行build在运行的话是当前目录!!! 23 | db, err := bolt.Open("chaorsBlock.db", 0600, nil) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | defer db.Close() 28 | ``` 29 | 30 | 在你打开之后,你有两种处理它的方式:读-写和只读操作,读-写方式开始于db.Update方法,常用于建表和表中插入新数据;只读操作开始于db.View方法,常用于表数据的查询。 31 | 32 | ##### 创建新表 33 | ``` 34 | //2.创建表 35 | err = db.Update(func(tx *bolt.Tx) error { 36 | 37 | //判断要创建的表是否存在 38 | b := tx.Bucket([]byte("MyBlocks")) 39 | if b == nil { 40 | 41 | //创建叫"MyBucket"的表 42 | _, err := tx.CreateBucket([]byte("MyBlocks")) 43 | if err != nil { 44 | //也可以在这里对表做插入操作 45 | log.Fatal(err) 46 | } 47 | } 48 | 49 | //一定要返回nil 50 | return nil 51 | }) 52 | 53 | //更新数据库失败 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | ``` 58 | 59 | ##### 更新表内容 60 | ``` 61 | //3.更新表数据 62 | err = db.Update(func(tx *bolt.Tx) error { 63 | 64 | //取出叫"MyBucket"的表 65 | b := tx.Bucket([]byte("MyBlocks")) 66 | 67 | //往表里面存储数据 68 | if b != nil { 69 | //插入的键值对数据类型必须是字节数组 70 | err := b.Put([]byte("l"), []byte("0x0000")) 71 | err := b.Put([]byte("ll"), []byte("0x0001")) 72 | err := b.Put([]byte("lll"), []byte("0x0002")) 73 | if err != nil { 74 | log.Fatal(err) 75 | } 76 | } 77 | 78 | //一定要返回nil 79 | return nil 80 | }) 81 | 82 | //更新数据库失败 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | ``` 87 | 88 | ##### 表查询 89 | ``` 90 | //4.查看表数据 91 | err = db.View(func(tx *bolt.Tx) error { 92 | 93 | //取出叫"MyBucket"的表 94 | b := tx.Bucket([]byte("MyBlocks")) 95 | 96 | //往表里面存储数据 97 | if b != nil { 98 | 99 | data := b.Get([]byte("l")) 100 | fmt.Printf("%s\n", data) 101 | data := b.Get([]byte("l")) 102 | fmt.Printf("%s\n", data) 103 | } 104 | 105 | //一定要返回nil 106 | return nil 107 | }) 108 | 109 | //查询数据库失败 110 | if err != nil { 111 | log.Fatal(err) 112 | } 113 | ``` 114 | 115 | boltdb基本使用就先学到这,搭建公链用数据库存储区块大概也只用到这么多,以后具体涉及到boltdb其他知识再针对性学习就好。 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /part3-boltdb-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part3-boltdb-Prototype/main -------------------------------------------------------------------------------- /part3-boltdb-Prototype/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: main.go 5 | 6 | @time: 2018/06/21 22:01 7 | 8 | @desc: boltdb存储区块信心 9 | */ 10 | 11 | 12 | package main 13 | 14 | import ( 15 | "chaors.com/LearnGo/publicChaorsChain/part3-boltdb-Prototype/BLC" 16 | "github.com/boltdb/bolt" 17 | "log" 18 | "fmt" 19 | ) 20 | 21 | func main() { 22 | 23 | blockchain := BLC.CreateBlockchainWithGensisBlock() 24 | //添加一个新区快 25 | blockchain.AddBlockToBlockchain("first Block", 26 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 27 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 28 | blockchain.AddBlockToBlockchain("second Block", 29 | blockchain.Blocks[len(blockchain.Blocks)-1].Height, 30 | blockchain.Blocks[len(blockchain.Blocks)-1].Hash) 31 | 32 | //1.数据库创建 33 | //在这里gland直接运行,生成的my.db在main.go上层目录;命令行build在运行的话是当前目录!!! 34 | db, err := bolt.Open("chaorsBlock.db", 0600, nil) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | defer db.Close() 39 | 40 | //2.创建表 41 | err = db.Update(func(tx *bolt.Tx) error { 42 | 43 | //创建叫"MyBucket"的表 44 | b := tx.Bucket([]byte("MyBlocks")) 45 | if b == nil { 46 | 47 | _, err := tx.CreateBucket([]byte("MyBlocks")) 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | } 52 | 53 | 54 | //一定要返回nil 55 | return nil 56 | }) 57 | 58 | //更新数据库失败 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | 63 | 64 | //3.更新表数据 65 | err = db.Update(func(tx *bolt.Tx) error { 66 | 67 | //取出叫"MyBucket"的表 68 | b := tx.Bucket([]byte("MyBlocks")) 69 | 70 | //往表里面存储数据 71 | if b != nil { 72 | 73 | err := b.Put(blockchain.Blocks[0].Hash, blockchain.Blocks[0].Serialize()) 74 | err = b.Put(blockchain.Blocks[1].Hash, blockchain.Blocks[1].Serialize()) 75 | err = b.Put(blockchain.Blocks[1].Hash, blockchain.Blocks[2].Serialize()) 76 | err = b.Put([]byte("headBlock"), blockchain.Blocks[2].Serialize()) 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | } 81 | 82 | //一定要返回nil 83 | return nil 84 | }) 85 | 86 | //更新数据库失败 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | 92 | //4.查看表数据 93 | err = db.View(func(tx *bolt.Tx) error { 94 | 95 | //取出叫"MyBucket"的表 96 | b := tx.Bucket([]byte("MyBlocks")) 97 | 98 | //往表里面存储数据 99 | if b != nil { 100 | 101 | data := b.Get(blockchain.Blocks[0].Hash) 102 | fmt.Printf("%v \n", BLC.DeSerializeBlock(data)) 103 | data = b.Get(blockchain.Blocks[1].Hash) 104 | fmt.Printf("%s\n", data) 105 | } 106 | 107 | //一定要返回nil 108 | return nil 109 | }) 110 | 111 | //更新数据库失败 112 | if err != nil { 113 | log.Fatal(err) 114 | } 115 | 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /part3-boltdb-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | boltdb存储区块信心 3 | **/ -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | ) 20 | 21 | type Block struct { 22 | //1.区块高度 23 | Height int64 24 | //2.上一个区块HAsh 25 | PrevBlockHash []byte 26 | //3.交易数据 27 | Data []byte 28 | //4.时间戳 29 | Timestamp int64 30 | //5.Hash 31 | Hash []byte 32 | //6.Nonce 符合工作量证明的随机数 33 | Nonce int64 34 | } 35 | 36 | //区块序列化 37 | func (block *Block) Serialize() []byte { 38 | 39 | var result bytes.Buffer 40 | encoder := gob.NewEncoder(&result) 41 | 42 | err := encoder.Encode(block) 43 | if err != nil{ 44 | 45 | log.Panic(err) 46 | } 47 | 48 | return result.Bytes() 49 | } 50 | 51 | //区块反序列化 52 | func DeSerializeBlock(blockBytes []byte) *Block { 53 | 54 | var block Block 55 | 56 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 57 | 58 | err := decoder.Decode(&block) 59 | if err != nil { 60 | 61 | log.Panic(err) 62 | } 63 | 64 | return &block 65 | } 66 | 67 | 68 | //1.创建新的区块 69 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 70 | 71 | //创建区块 72 | block := &Block{ 73 | Height: height, 74 | PrevBlockHash: prevBlockHash, 75 | Data: []byte(data), 76 | Timestamp: time.Now().Unix(), 77 | Hash: nil, 78 | Nonce: 0} 79 | 80 | //调用工作量证明返回有效的Hash 81 | pow := NewProofOfWork(block) 82 | hash, nonce := pow.Run() 83 | block.Hash = hash[:] 84 | block.Nonce = nonce 85 | 86 | fmt.Printf("\r######%d-%x\n", nonce, hash) 87 | 88 | return block 89 | } 90 | 91 | //单独方法生成创世区块 92 | func CreateGenesisBlock(data string) *Block { 93 | 94 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 95 | } 96 | -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | //数据库查询 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error { 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | if b != nil { 24 | 25 | //获取当前迭代器对应的区块 26 | currBlockBytes := b.Get(blcIterator.CurrHash) 27 | block = DeSerializeBlock(currBlockBytes) 28 | 29 | //更新迭代器 30 | blcIterator.CurrHash = block.PrevBlockHash 31 | 32 | } 33 | return nil 34 | }) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | return block 40 | } 41 | 42 | -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 19 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.Data, 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | for { 87 | //准备数据 88 | dataBytes := proofOfWork.prepareData(nonce) 89 | //生成Hash 90 | hash = sha256.Sum256(dataBytes) 91 | 92 | //\r将当前打印行覆盖 93 | fmt.Printf("\r%x", hash) 94 | //存储Hash到hashInt 95 | hashInt.SetBytes(hash[:]) 96 | //验证Hash 97 | if proofOfWork.target.Cmp(&hashInt) == 1 { 98 | 99 | break 100 | } 101 | nonce++ 102 | } 103 | 104 | return hash[:], int64(nonce) 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part4-DataPersistence-Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part4-DataPersistence-Prototype/main -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/main.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: main.go 5 | 6 | @time: 2018/06/21 22:01 7 | 8 | @desc: boltdb存储区块信心 9 | */ 10 | 11 | package main 12 | 13 | import ( 14 | "chaors.com/LearnGo/publicChaorsChain/part4-DataPersistence-Prototype/BLC" 15 | ) 16 | 17 | func main() { 18 | 19 | blockchain := BLC.CreateBlockchainWithGensisBlock() 20 | defer blockchain.DB.Close() 21 | 22 | //添加一个新区快 23 | blockchain.AddBlockToBlockchain("4th Block") 24 | blockchain.AddBlockToBlockchain("5th Block") 25 | //blockchain.AddBlockToBlockchain("third Block") 26 | 27 | blockchain.Printchain() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /part4-DataPersistence-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 区块链数据持久化 3 | **/ -------------------------------------------------------------------------------- /part5-cli-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | ) 20 | 21 | type Block struct { 22 | //1.区块高度 23 | Height int64 24 | //2.上一个区块HAsh 25 | PrevBlockHash []byte 26 | //3.交易数据 27 | Data []byte 28 | //4.时间戳 29 | Timestamp int64 30 | //5.Hash 31 | Hash []byte 32 | //6.Nonce 符合工作量证明的随机数 33 | Nonce int64 34 | } 35 | 36 | //区块序列化 37 | func (block *Block) Serialize() []byte { 38 | 39 | var result bytes.Buffer 40 | encoder := gob.NewEncoder(&result) 41 | 42 | err := encoder.Encode(block) 43 | if err != nil{ 44 | 45 | log.Panic(err) 46 | } 47 | 48 | return result.Bytes() 49 | } 50 | 51 | //区块序列化 52 | func DeSerializeBlock(blockBytes []byte) *Block { 53 | 54 | var block Block 55 | 56 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 57 | 58 | err := decoder.Decode(&block) 59 | if err != nil { 60 | 61 | log.Panic(err) 62 | } 63 | 64 | return &block 65 | } 66 | 67 | 68 | //1.创建新的区块 69 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 70 | 71 | //创建区块 72 | block := &Block{ 73 | Height: height, 74 | PrevBlockHash: prevBlockHash, 75 | Data: []byte(data), 76 | Timestamp: time.Now().Unix(), 77 | Hash: nil, 78 | Nonce: 0} 79 | 80 | //调用工作量证明返回有效的Hash 81 | pow := NewProofOfWork(block) 82 | hash, nonce := pow.Run() 83 | block.Hash = hash[:] 84 | block.Nonce = nonce 85 | 86 | fmt.Printf("\r######%d-%x\n", nonce, hash) 87 | 88 | return block 89 | } 90 | 91 | //单独方法生成创世区块 92 | func CreateGenesisBlock(data string) *Block { 93 | 94 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 95 | } 96 | -------------------------------------------------------------------------------- /part5-cli-Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | //数据库查询 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error { 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | if b != nil { 24 | 25 | //获取当前迭代器对应的区块 26 | currBlockBytes := b.Get(blcIterator.CurrHash) 27 | block = DeSerializeBlock(currBlockBytes) 28 | 29 | //更新迭代器 30 | blcIterator.CurrHash = block.PrevBlockHash 31 | 32 | } 33 | return nil 34 | }) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | return block 40 | } 41 | 42 | -------------------------------------------------------------------------------- /part5-cli-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 16 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.Data, 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | for { 87 | //准备数据 88 | dataBytes := proofOfWork.prepareData(nonce) 89 | //生成Hash 90 | hash = sha256.Sum256(dataBytes) 91 | 92 | //\r将当前打印行覆盖 93 | fmt.Printf("\r%x", hash) 94 | //存储Hash到hashInt 95 | hashInt.SetBytes(hash[:]) 96 | //验证Hash 97 | if proofOfWork.target.Cmp(&hashInt) == 1 { 98 | 99 | break 100 | } 101 | nonce++ 102 | } 103 | 104 | return hash[:], int64(nonce) 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /part5-cli-Prototype/BLC/cli.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | ) 9 | 10 | type CLI struct { 11 | //添加cli命令行工具的类 12 | Blockchain *Blockchain 13 | } 14 | 15 | //打印目前左右命令使用方法 16 | func printUsage() { 17 | fmt.Println("Usage:") 18 | fmt.Println("\tcreateBlockchainWithGensisBlock -data Data 创世信息 创建创世区块") 19 | fmt.Println("\taddBlock -data DATA 交易信息 新增区块") 20 | fmt.Println("\tprintchain 打印所有区块信息") 21 | } 22 | 23 | func isValidArgs() { 24 | 25 | //获取当前输入参数个数 26 | if len(os.Args) < 2 { 27 | printUsage() 28 | os.Exit(1) 29 | } 30 | } 31 | 32 | //新增区块 33 | func (cli *CLI) addBlock(data string) { 34 | cli.Blockchain.AddBlockToBlockchain(data) 35 | 36 | } 37 | 38 | //打印区块链 39 | func (cli *CLI) printchain() { 40 | cli.Blockchain.Printchain() 41 | } 42 | 43 | func (cli *CLI) Run() { 44 | 45 | isValidArgs() 46 | 47 | //自定义cli命令 48 | addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) 49 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 50 | 51 | //addBlockCmd 设置默认参数 52 | flagAddBlockData := addBlockCmd.String("data", "chaors", "交易数据") 53 | 54 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 55 | switch os.Args[1] { 56 | case "addBlock": 57 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 58 | err := addBlockCmd.Parse(os.Args[2:]) 59 | if err != nil { 60 | log.Panic(err) 61 | } 62 | case "printchain": 63 | err := printchainCmd.Parse(os.Args[2:]) 64 | if err != nil { 65 | log.Panic(err) 66 | } 67 | default: 68 | printUsage() 69 | os.Exit(1) 70 | } 71 | 72 | //对addBlockCmd命令的解析 73 | if addBlockCmd.Parsed() { 74 | 75 | if *flagAddBlockData == "" { 76 | 77 | printUsage() 78 | os.Exit(1) 79 | } 80 | 81 | //这里真正地调用新增区块方法 82 | cli.addBlock(*flagAddBlockData) 83 | } 84 | //对printchainCmd命令的解析 85 | if printchainCmd.Parsed() { 86 | 87 | cli.printchain() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /part5-cli-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part5-cli-Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part5-cli-Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part5-cli-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part5-cli-Prototype/main -------------------------------------------------------------------------------- /part5-cli-Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part5-cli-Prototype/BLC" 6 | //"fmt" 7 | //"os" 8 | //"flag" 9 | //"log" 10 | ) 11 | 12 | func main() { 13 | 14 | 15 | //fmt.Printf("%v\n", BLC.IsDBExists("chaorsBlockchain.db")) 16 | blockchain := BLC.CreateBlockchainWithGensisBlock() 17 | //fmt.Printf("%v\n", BLC.IsDBExists("chaorsBlockchain.db")) 18 | 19 | //blockchain.Printchain() 20 | cli := BLC.CLI{blockchain} 21 | cli.Run() 22 | 23 | 24 | //defer blockchain.DB.Close() 25 | 26 | 27 | /** 28 | addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) 29 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 30 | 31 | flagAddBlockData := addBlockCmd.String("data", "chaors", "交易数据") 32 | switch os.Args[1] { 33 | case "addBlock": 34 | err := addBlockCmd.Parse(os.Args[2:]) 35 | if err != nil { 36 | log.Panic(err) 37 | } 38 | case "printchain": 39 | err := addBlockCmd.Parse(os.Args[2:]) 40 | if err != nil { 41 | log.Panic(err) 42 | } 43 | default: 44 | printUsage() 45 | os.Exit(1) 46 | } 47 | 48 | if addBlockCmd.Parsed() { 49 | 50 | if *flagAddBlockData == "" { 51 | 52 | printUsage() 53 | os.Exit(1) 54 | } 55 | 56 | fmt.Println(*flagAddBlockData) 57 | } 58 | if printchainCmd.Parsed() { 59 | 60 | fmt.Println("输出所有区块信息") 61 | } 62 | */ 63 | 64 | 65 | //args := os.Args 66 | //fmt.Printf("%v\n", args) 67 | 68 | //定义一个字符串变量,并制定默认值以及使用方式 69 | //flagStr := flag.String("printchain", "", "输出所有区块信息") 70 | // 71 | ////定义一个int型字符 默认值为6 72 | //flagInt := flag.Int("number", 6, "输出一个数") 73 | // 74 | ////定义一个bool型变量 默认值为false 75 | //flagBool := flag.Bool("open", false, "判断真假") 76 | // 77 | ////flag解析 78 | //flag.Parse() 79 | // 80 | //fmt.Printf("%s\n", *flagStr) 81 | //fmt.Printf("%d\n", *flagInt) 82 | //fmt.Printf("%v\n", *flagBool) 83 | 84 | 85 | // 86 | ////添加一个新区快 87 | //blockchain.AddBlockToBlockchain("first Block") 88 | //blockchain.AddBlockToBlockchain("second Block") 89 | //blockchain.AddBlockToBlockchain("third Block") 90 | // 91 | //blockchain.Printchain() 92 | 93 | } 94 | -------------------------------------------------------------------------------- /part5-cli-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | cli命令行工具 3 | **/ -------------------------------------------------------------------------------- /part6-cli1-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | ) 20 | 21 | type Block struct { 22 | //1.区块高度 23 | Height int64 24 | //2.上一个区块HAsh 25 | PrevBlockHash []byte 26 | //3.交易数据 27 | Data []byte 28 | //4.时间戳 29 | Timestamp int64 30 | //5.Hash 31 | Hash []byte 32 | //6.Nonce 符合工作量证明的随机数 33 | Nonce int64 34 | } 35 | 36 | //区块序列化 37 | func (block *Block) Serialize() []byte { 38 | 39 | var result bytes.Buffer 40 | encoder := gob.NewEncoder(&result) 41 | 42 | err := encoder.Encode(block) 43 | if err != nil{ 44 | 45 | log.Panic(err) 46 | } 47 | 48 | return result.Bytes() 49 | } 50 | 51 | //区块序列化 52 | func DeSerializeBlock(blockBytes []byte) *Block { 53 | 54 | var block Block 55 | 56 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 57 | 58 | err := decoder.Decode(&block) 59 | if err != nil { 60 | 61 | log.Panic(err) 62 | } 63 | 64 | return &block 65 | } 66 | 67 | 68 | //1.创建新的区块 69 | func NewBlock(data string, height int64, prevBlockHash []byte) *Block { 70 | 71 | //创建区块 72 | block := &Block{ 73 | Height: height, 74 | PrevBlockHash: prevBlockHash, 75 | Data: []byte(data), 76 | Timestamp: time.Now().Unix(), 77 | Hash: nil, 78 | Nonce: 0} 79 | 80 | //调用工作量证明返回有效的Hash 81 | pow := NewProofOfWork(block) 82 | hash, nonce := pow.Run() 83 | block.Hash = hash[:] 84 | block.Nonce = nonce 85 | 86 | fmt.Printf("\r######%d-%x\n", nonce, hash) 87 | 88 | return block 89 | } 90 | 91 | //单独方法生成创世区块 92 | func CreateGenesisBlock(data string) *Block { 93 | 94 | return NewBlock(data, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 95 | } 96 | -------------------------------------------------------------------------------- /part6-cli1-Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | //数据库查询 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error { 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | if b != nil { 24 | 25 | //获取当前迭代器对应的区块 26 | currBlockBytes := b.Get(blcIterator.CurrHash) 27 | block = DeSerializeBlock(currBlockBytes) 28 | 29 | //更新迭代器 30 | blcIterator.CurrHash = block.PrevBlockHash 31 | 32 | } 33 | return nil 34 | }) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | return block 40 | } 41 | 42 | -------------------------------------------------------------------------------- /part6-cli1-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.Data, 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part6-cli1-Prototype/BLC/cli.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | ) 9 | 10 | type CLI struct { 11 | 12 | } 13 | 14 | //打印目前左右命令使用方法 15 | func printUsage() { 16 | fmt.Println("Usage:") 17 | fmt.Println("\tcreateBlockchain -data Data 创世信息 创建创世区块") 18 | fmt.Println("\taddBlock -data DATA 交易信息 新增区块") 19 | fmt.Println("\tprintchain 打印所有区块信息") 20 | } 21 | 22 | func isValidArgs() { 23 | 24 | //获取当前输入参数个数 25 | if len(os.Args) < 2 { 26 | printUsage() 27 | os.Exit(1) 28 | } 29 | } 30 | 31 | //新增区块 32 | func (cli *CLI) addBlock(data string) { 33 | 34 | blockchain := GetBlockchain() 35 | defer blockchain.DB.Close() 36 | 37 | blockchain.AddBlockToBlockchain(data) 38 | 39 | } 40 | 41 | //打印区块链 42 | func (cli *CLI) printchain() { 43 | 44 | blockchain := GetBlockchain() 45 | defer blockchain.DB.Close() 46 | 47 | blockchain.Printchain() 48 | } 49 | 50 | //新建区块链 51 | func (cli *CLI)creatBlockchain(data string) { 52 | 53 | CreateBlockchainWithGensisBlock(data) 54 | } 55 | 56 | func (cli *CLI) Run() { 57 | 58 | isValidArgs() 59 | 60 | //自定义cli命令 61 | addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) 62 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 63 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 64 | 65 | //addBlockCmd 设置默认参数 66 | flagAddBlockData := addBlockCmd.String("data", "", "交易数据") 67 | flagCreateBlockchainData := createBlockchainCmd.String("data", "gensis block...", "创世区块信息") 68 | 69 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 70 | switch os.Args[1] { 71 | case "addBlock": 72 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 73 | err := addBlockCmd.Parse(os.Args[2:]) 74 | if err != nil { 75 | log.Panic(err) 76 | } 77 | case "printchain": 78 | err := printchainCmd.Parse(os.Args[2:]) 79 | if err != nil { 80 | log.Panic(err) 81 | } 82 | case "createBlockchain": 83 | err := createBlockchainCmd.Parse(os.Args[2:]) 84 | if err != nil { 85 | log.Panic(err) 86 | } 87 | default: 88 | printUsage() 89 | os.Exit(1) 90 | } 91 | 92 | //对addBlockCmd命令的解析 93 | if addBlockCmd.Parsed() { 94 | 95 | if *flagAddBlockData == "" { 96 | 97 | printUsage() 98 | os.Exit(1) 99 | } 100 | 101 | //这里真正地调用新增区块方法 102 | cli.addBlock(*flagAddBlockData) 103 | } 104 | //对printchainCmd命令的解析 105 | if printchainCmd.Parsed() { 106 | 107 | cli.printchain() 108 | } 109 | // 110 | if createBlockchainCmd.Parsed() { 111 | 112 | if *flagCreateBlockchainData == "" { 113 | 114 | cli.creatBlockchain("gensis block") 115 | } 116 | 117 | cli.creatBlockchain(*flagCreateBlockchainData) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /part6-cli1-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part6-cli1-Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part6-cli1-Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part6-cli1-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part6-cli1-Prototype/main -------------------------------------------------------------------------------- /part6-cli1-Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part6-cli1-Prototype/BLC" 6 | //"fmt" 7 | //"os" 8 | //"flag" 9 | //"log" 10 | ) 11 | 12 | func main() { 13 | 14 | cli := BLC.CLI{} 15 | cli.Run() 16 | } 17 | -------------------------------------------------------------------------------- /part6-cli1-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | cli命令行工具 3 | **/ -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | "crypto/sha256" 20 | ) 21 | 22 | type Block struct { 23 | //1.区块高度 24 | Height int64 25 | //2.上一个区块HAsh 26 | PrevBlockHash []byte 27 | //3.交易数据 28 | Txs [] *Transaction 29 | //4.时间戳 30 | Timestamp int64 31 | //5.Hash 32 | Hash []byte 33 | //6.Nonce 符合工作量证明的随机数 34 | Nonce int64 35 | } 36 | 37 | //区块序列化 38 | func (block *Block) Serialize() []byte { 39 | 40 | var result bytes.Buffer 41 | encoder := gob.NewEncoder(&result) 42 | 43 | err := encoder.Encode(block) 44 | if err != nil{ 45 | 46 | log.Panic(err) 47 | } 48 | 49 | return result.Bytes() 50 | } 51 | 52 | //区块反序列化 53 | func DeSerializeBlock(blockBytes []byte) *Block { 54 | 55 | var block Block 56 | 57 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 58 | 59 | err := decoder.Decode(&block) 60 | if err != nil { 61 | 62 | log.Panic(err) 63 | } 64 | 65 | return &block 66 | } 67 | 68 | 69 | //1.创建新的区块 70 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 71 | 72 | //创建区块 73 | block := &Block{ 74 | Height: height, 75 | PrevBlockHash: prevBlockHash, 76 | Txs: txs, 77 | Timestamp: time.Now().Unix(), 78 | Hash: nil, 79 | Nonce: 0} 80 | 81 | //调用工作量证明返回有效的Hash 82 | pow := NewProofOfWork(block) 83 | hash, nonce := pow.Run() 84 | block.Hash = hash[:] 85 | block.Nonce = nonce 86 | 87 | fmt.Printf("\r######%d-%x\n", nonce, hash) 88 | 89 | return block 90 | } 91 | 92 | //单独方法生成创世区块 93 | func CreateGenesisBlock(txs []*Transaction) *Block { 94 | 95 | return NewBlock(txs, 1, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 96 | } 97 | 98 | //将交易信息转换为字节数组 99 | func (block *Block) HashTransactions() []byte { 100 | 101 | var txHashes [][]byte 102 | var txHash [32]byte 103 | 104 | for _, tx := range block.Txs { 105 | 106 | txHashes = append(txHashes, tx.TxHAsh) 107 | } 108 | 109 | txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 110 | 111 | return txHash[:] 112 | } -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | //数据库查询 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error { 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | if b != nil { 24 | 25 | //获取当前迭代器对应的区块 26 | currBlockBytes := b.Get(blcIterator.CurrHash) 27 | block = DeSerializeBlock(currBlockBytes) 28 | 29 | //更新迭代器 30 | blcIterator.CurrHash = block.PrevBlockHash 31 | 32 | } 33 | return nil 34 | }) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | return block 40 | } 41 | 42 | -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type TXInput struct { 4 | //交易ID 5 | TxHash []byte 6 | //存储TXOutput在Vout里的索引 7 | Vout int 8 | //数字签名 9 | ScriptSig string 10 | } -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type TXOutput struct { 4 | //面值 5 | Value int64 6 | //暂时理解为用户名 7 | ScriptPubKey string 8 | } 9 | 10 | -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/Transaction.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "crypto/sha256" 5 | "bytes" 6 | "encoding/gob" 7 | "log" 8 | ) 9 | 10 | type Transaction struct { 11 | //1.交易哈希值 12 | TxHAsh []byte 13 | //2.输入 14 | Vins []*TXInput 15 | //3.输出 16 | Vouts []*TXOutput 17 | } 18 | 19 | //1.coinbaseTransaction 20 | //2.转账时产生的Transaction 21 | 22 | /** 23 | 24 | 举个简单的🌰,我们先把复杂问题简单化,假设每个区块里只有一个交易。 25 | 1.节点chaors挖到创世区块,产生25BTC的创币交易。由于是创世区块,其本身产生之前是没有 26 | 交易的,所以在输入对象TXInput的交易哈希为空,vount所在的下标为-1,数字签名为空或者 27 | 随便填写;输出对象里btc拥有者为chaors,面值为25btc 28 | 29 | 创世区块交易结构 30 | txInput0 = &TXInput{[]byte{},-1,"Gensis Block"} 31 | txOutput0 = &TXOutput{25, "chaors"} 索引为0 32 | 33 | Transaction{"00000", 34 | []*TXInput{txInput0}, 35 | []*TXOutput{txOutput0} 36 | } 37 | 38 | 2.chaors获得25btc后,他的好友ww知道后向他索要10btc.大方的chaors便把10btc转给ww.此时 39 | 交易的输入为chaors上笔交易获得的btc,TXInput对象的交易ID为奖励chaors的上一个交易ID,vount下标 40 | 为chaors的TXOutput下标,签名此时且认为是来自chaors,填作"chaors" 41 | 此时chaors的25btc面值的TXOutput就被花费不复存在了,那么chaors还应该有15btc的找零哪去了? 42 | 系统会为chaors的找零新生成一个面值15btc的TXOutput。所以,这次有一个输入,两个输出。 43 | 44 | 第二个区块交易结构(假设只有一笔交易) 45 | chaors(25) 给 ww 转 10 -- >> chaors(15) + ww(10) 46 | 47 | 输入 48 | txInput1 = &TXInput{"00000",0,"chaors"} 49 | "00000" 相当于来自于哈希为"00000"的交易 50 | 索引为零,相当于上一次的txOutput0为输入 51 | 52 | 输出 53 | txOutput1 = &TXOutput{10, "ww"} 索引为1 chaors转给ww的10btc产生的输出 54 | txOutput2 = &TXOutput{15, "chaors"} 索引为2 给ww转账产生的找零 55 | Transaction{"11111", 56 | []*TXInput{txInput1} 57 | []*TXOutput{txOutput1, txOutput2} 58 | } 59 | 60 | 3.ww感觉拥有比特币是一件很酷的事情,又来跟chaors要。出于兄弟情谊,chaors又转给ww7btc 61 | 62 | 第三个区块交易结构 63 | 输入 64 | txInput2 = &TXInput{"11111",2,"chaors"} 65 | 66 | 输出 67 | txOutput3 = &TXOutput{7, "ww"} 索引为3 68 | txOutput4 = &TXOutput{8, "chaors"} 索引为4 69 | Transaction{"22222", 70 | []*TXInput{txInput2} 71 | []*TXOutput{txOutput3, txOutput4} 72 | } 73 | 74 | 4.消息传到他们共同的朋友xyz那里,xyz觉得btc很好玩向ww索要15btc.ww一向害怕xyx,于是 75 | 尽管不愿意也只能屈服。我们来看看ww此时的全部财产: 76 | txOutput1 = &TXOutput{10, "ww"} 索引为1 来自交易"11111" 77 | txOutput3 = &TXOutput{7, "ww"} 索引为3 来自交易"22222" 78 | 想要转账15btc,ww的哪一笔txOutput都不够,这个时候就需要用ww的两个txOutput都作为 79 | 输入: 80 | 81 | txInput3 = &TXInput{"11111",1,"ww"} 82 | txInput4 = &TXInput{"22222",3,"ww"} 83 | 84 | 85 | 输出 86 | txOutput5 = &TXOutput{15, "xyz"} 索引为5 87 | txOutput6 = &TXOutput{2, "ww"} 索引为6 88 | 89 | 第四个区块交易结构 90 | Transaction{"33333", 91 | []*TXInput{txInput3, txInput4} 92 | []*TXOutput{txOutput5, txOutput6} 93 | } 94 | 95 | 经过以上交易,chaors最后只剩下面值为8的TXOutput4,txOutput0和txOutput2都在给ww 96 | 的转账中花费;ww最后只剩下面值为2的txOutput6,txOutput3和txOutput4在给xyx的转账 97 | 中花费。由此可见,区块链转账中的UTXO,只要发生交易就不复存在,只会形成新的UTXO 98 | 给新的地址;如果有找零,会产生新的UTXO给原有地址。 99 | */ 100 | 101 | func NewCoinbaseTransaction(address string) *Transaction{ 102 | 103 | //输入 由于创世区块其实没有输入,所以交易哈希传空,TXOutput索引传-1,签名随你 104 | txInput := &TXInput{[]byte{}, -1, "CoinbaseTransaction"} 105 | //输出 产生一笔奖励给挖矿者 106 | txOutput := &TXOutput{25, address} 107 | txCoinbase := &Transaction{ 108 | []byte{}, 109 | []*TXInput{txInput}, 110 | []*TXOutput{txOutput}, 111 | } 112 | 113 | txCoinbase.HashTransactions() 114 | 115 | return txCoinbase 116 | } 117 | 118 | 119 | //将交易信息转换为字节数组 120 | func (tx *Transaction) HashTransactions() { 121 | 122 | //交易信息序列化 123 | var result bytes.Buffer 124 | encoder := gob.NewEncoder(&result) 125 | 126 | err := encoder.Encode(tx) 127 | if err != nil { 128 | 129 | log.Panic(err) 130 | } 131 | 132 | //设置hash 133 | txHash := sha256.Sum256(result.Bytes()) 134 | tx.TxHAsh = txHash[:] 135 | } -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/cli.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | ) 9 | 10 | type CLI struct { 11 | 12 | } 13 | 14 | //打印目前左右命令使用方法 15 | func printUsage() { 16 | fmt.Println("Usage:") 17 | fmt.Println("\tcreateBlockchain -address --创世区块地址 ") 18 | fmt.Println("\taddBlock -data DATA --交易信息 新增区块") 19 | fmt.Println("\tprintchain --打印所有区块信息") 20 | } 21 | 22 | func isValidArgs() { 23 | 24 | //获取当前输入参数个数 25 | if len(os.Args) < 2 { 26 | printUsage() 27 | os.Exit(1) 28 | } 29 | } 30 | 31 | //新增区块 32 | func (cli *CLI) addBlock(data string ) { 33 | 34 | blockchain := GetBlockchain() 35 | defer blockchain.DB.Close() 36 | 37 | blockchain.AddBlockToBlockchain([]*Transaction{}) 38 | 39 | } 40 | 41 | //打印区块链 42 | func (cli *CLI) printchain() { 43 | 44 | blockchain := GetBlockchain() 45 | defer blockchain.DB.Close() 46 | 47 | blockchain.Printchain() 48 | } 49 | 50 | //新建区块链 51 | func (cli *CLI)creatBlockchain(address string) { 52 | 53 | CreateBlockchainWithGensisBlock(address) 54 | } 55 | 56 | func (cli *CLI) Run() { 57 | 58 | isValidArgs() 59 | 60 | //自定义cli命令 61 | addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) 62 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 63 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 64 | 65 | //addBlockCmd 设置默认参数 66 | flagAddBlockData := addBlockCmd.String("data", "", "交易数据") 67 | flagCreateBlockchainAddress := createBlockchainCmd.String("address", "", "创世区块地址") 68 | 69 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 70 | switch os.Args[1] { 71 | case "addBlock": 72 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 73 | err := addBlockCmd.Parse(os.Args[2:]) 74 | if err != nil { 75 | log.Panic(err) 76 | } 77 | case "printchain": 78 | err := printchainCmd.Parse(os.Args[2:]) 79 | if err != nil { 80 | log.Panic(err) 81 | } 82 | case "createBlockchain": 83 | err := createBlockchainCmd.Parse(os.Args[2:]) 84 | if err != nil { 85 | log.Panic(err) 86 | } 87 | default: 88 | printUsage() 89 | os.Exit(1) 90 | } 91 | 92 | //对addBlockCmd命令的解析 93 | if addBlockCmd.Parsed() { 94 | 95 | if *flagAddBlockData == "" { 96 | 97 | printUsage() 98 | os.Exit(1) 99 | } 100 | 101 | //这里真正地调用新增区块方法 102 | cli.addBlock(*flagAddBlockData) 103 | } 104 | //对printchainCmd命令的解析 105 | if printchainCmd.Parsed() { 106 | 107 | cli.printchain() 108 | } 109 | // 110 | if createBlockchainCmd.Parsed() { 111 | 112 | if *flagCreateBlockchainAddress == "" { 113 | 114 | cli.creatBlockchain(*flagCreateBlockchainAddress) 115 | } 116 | 117 | cli.creatBlockchain(*flagCreateBlockchainAddress) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /part7-transaction-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | ) 19 | 20 | //将int64转换为bytes 21 | func IntToHex(num int64) []byte { 22 | 23 | buff := new(bytes.Buffer) 24 | err := binary.Write(buff, binary.BigEndian, num) 25 | if err != nil { 26 | 27 | log.Panic(err) 28 | } 29 | 30 | return buff.Bytes() 31 | } -------------------------------------------------------------------------------- /part7-transaction-Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part7-transaction-Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part7-transaction-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part7-transaction-Prototype/main -------------------------------------------------------------------------------- /part7-transaction-Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part7-transaction-Prototype/BLC" 6 | //"fmt" 7 | //"os" 8 | //"flag" 9 | //"log" 10 | ) 11 | 12 | func main() { 13 | 14 | cli := BLC.CLI{} 15 | cli.Run() 16 | } 17 | -------------------------------------------------------------------------------- /part7-transaction-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 交易 3 | 1.Transaction 4 | 2.UTXO简单定义 5 | 3.coinbaseTransaction 6 | 4.交易信息打印 7 | **/ -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | "crypto/sha256" 20 | ) 21 | 22 | type Block struct { 23 | //1.区块高度 24 | Height int64 25 | //2.上一个区块HAsh 26 | PrevBlockHash []byte 27 | //3.交易数据 28 | Txs [] *Transaction 29 | //4.时间戳 30 | Timestamp int64 31 | //5.Hash 32 | Hash []byte 33 | //6.Nonce 符合工作量证明的随机数 34 | Nonce int64 35 | } 36 | 37 | //区块序列化 38 | func (block *Block) Serialize() []byte { 39 | 40 | var result bytes.Buffer 41 | 42 | encoder := gob.NewEncoder(&result) 43 | 44 | err := encoder.Encode(block) 45 | if err != nil { 46 | log.Panic(err) 47 | } 48 | 49 | return result.Bytes() 50 | } 51 | 52 | //区块反序列化 53 | func DeSerializeBlock(blockBytes []byte) *Block { 54 | 55 | var block Block 56 | 57 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 58 | 59 | err := decoder.Decode(&block) 60 | if err != nil { 61 | log.Panic(err) 62 | } 63 | 64 | return &block 65 | } 66 | 67 | 68 | //1.创建新的区块 69 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 70 | 71 | //创建区块 72 | block := &Block{ 73 | Height: height, 74 | PrevBlockHash: prevBlockHash, 75 | Txs: txs, 76 | Timestamp: time.Now().Unix(), 77 | Hash: nil, 78 | Nonce: 0} 79 | 80 | //调用工作量证明返回有效的Hash 81 | pow := NewProofOfWork(block) 82 | hash, nonce := pow.Run() 83 | block.Hash = hash[:] 84 | block.Nonce = nonce 85 | 86 | fmt.Printf("\r######%d-%x\n", nonce, hash) 87 | 88 | return block 89 | } 90 | 91 | //单独方法生成创世区块 92 | func CreateGenesisBlock(txs []*Transaction) *Block { 93 | 94 | return NewBlock( 95 | txs, 96 | 1, 97 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 98 | ) 99 | } 100 | 101 | // 需要将Txs转换成[]byte 102 | func (block *Block) HashTransactions() []byte { 103 | 104 | var txHashes [][]byte 105 | var txHash [32]byte 106 | 107 | for _, tx := range block.Txs { 108 | 109 | txHashes = append(txHashes, tx.TxHAsh) 110 | } 111 | txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 112 | 113 | return txHash[:] 114 | 115 | } 116 | -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error{ 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | 24 | if b != nil { 25 | currentBloclBytes := b.Get(blcIterator.CurrHash) 26 | 27 | // 获取到当前迭代器里面的currentHash所对应的区块 28 | block = DeSerializeBlock(currentBloclBytes) 29 | 30 | // 更新迭代器里面CurrentHash 31 | blcIterator.CurrHash = block.PrevBlockHash 32 | } 33 | 34 | return nil 35 | }) 36 | if err != nil { 37 | log.Panic(err) 38 | } 39 | 40 | return block 41 | } 42 | 43 | -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type TXInput struct { 4 | //交易ID 5 | TxHash []byte 6 | //存储TXOutput在Vouts里的索引 7 | Vout int 8 | //数字签名 9 | ScriptSig string 10 | } 11 | 12 | //验证当前输入是否是当前地址的 13 | func (txInput *TXInput) UnlockWithAddress(address string) bool { 14 | 15 | return txInput.ScriptSig == address 16 | } -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type TXOutput struct { 4 | //面值 5 | Value int64 6 | //暂时理解为用户名 7 | ScriptPubKey string 8 | } 9 | 10 | // 11 | func (txOutput *TXOutput) UnLockScriptPubKeyWithAddress(address string) bool { 12 | 13 | return txOutput.ScriptPubKey == address 14 | } 15 | 16 | -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/cli.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | ) 9 | 10 | type CLI struct { 11 | 12 | } 13 | 14 | //打印目前左右命令使用方法 15 | func printUsage() { 16 | fmt.Println("Usage:") 17 | fmt.Println("\tcreateBlockchain -address --创世区块地址 ") 18 | fmt.Println("\tsend -from FROM -to TO -amount AMOUNT --交易明细") 19 | fmt.Println("\tprintchain --打印所有区块信息") 20 | fmt.Println("\tgetbalance -address -- 输出区块信息.") 21 | } 22 | 23 | func isValidArgs() { 24 | 25 | //获取当前输入参数个数 26 | if len(os.Args) < 2 { 27 | printUsage() 28 | os.Exit(1) 29 | } 30 | } 31 | 32 | 33 | //打印区块链 34 | func (cli *CLI) printchain() { 35 | 36 | blockchain := GetBlockchain() 37 | defer blockchain.DB.Close() 38 | 39 | blockchain.Printchain() 40 | } 41 | 42 | //新建区块链 43 | func (cli *CLI)creatBlockchain(address string) { 44 | 45 | blockchain := CreateBlockchainWithGensisBlock(address) 46 | defer blockchain.DB.Close() 47 | } 48 | 49 | //转账 50 | func (cli *CLI) send(from []string, to []string, amount []string) { 51 | 52 | blockchain := GetBlockchain() 53 | defer blockchain.DB.Close() 54 | 55 | blockchain.MineNewBlock(from, to, amount) 56 | } 57 | 58 | // 59 | func (cli *CLI) getBlance(address string) { 60 | 61 | fmt.Println("地址:" + address) 62 | 63 | blockchain := GetBlockchain() 64 | defer blockchain.DB.Close() 65 | 66 | amount := blockchain.GetBalance(address) 67 | 68 | fmt.Printf("%s一共有%d个Token\n", address, amount) 69 | 70 | } 71 | 72 | func (cli *CLI) Run() { 73 | 74 | isValidArgs() 75 | 76 | //自定义cli命令 77 | sendBlockCmd := flag.NewFlagSet("send", flag.ExitOnError) 78 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 79 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 80 | blanceBlockCmd := flag.NewFlagSet("getBalance", flag.ExitOnError) 81 | 82 | //addBlockCmd 设置默认参数 83 | flagSendBlockFrom := sendBlockCmd.String("from", "", "源地址") 84 | flagSendBlockTo := sendBlockCmd.String("to", "", "目标地址") 85 | flagSendBlockAmount := sendBlockCmd.String("amount", "", "转账金额") 86 | flagCreateBlockchainAddress := createBlockchainCmd.String("address", "", "创世区块地址") 87 | flagBlanceBlockAddress := blanceBlockCmd.String("address", "", "输出区块信息") 88 | 89 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 90 | switch os.Args[1] { 91 | case "send": 92 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 93 | err := sendBlockCmd.Parse(os.Args[2:]) 94 | if err != nil { 95 | log.Panic(err) 96 | } 97 | case "printchain": 98 | err := printchainCmd.Parse(os.Args[2:]) 99 | if err != nil { 100 | log.Panic(err) 101 | } 102 | case "createBlockchain": 103 | err := createBlockchainCmd.Parse(os.Args[2:]) 104 | if err != nil { 105 | log.Panic(err) 106 | } 107 | case "getBalance": 108 | err := blanceBlockCmd.Parse(os.Args[2:]) 109 | if err != nil { 110 | log.Panic(err) 111 | } 112 | default: 113 | printUsage() 114 | os.Exit(1) 115 | } 116 | 117 | //对addBlockCmd命令的解析 118 | if sendBlockCmd.Parsed() { 119 | 120 | if *flagSendBlockFrom == "" { 121 | 122 | printUsage() 123 | os.Exit(1) 124 | } 125 | if *flagSendBlockTo == "" { 126 | 127 | printUsage() 128 | os.Exit(1) 129 | } 130 | if *flagSendBlockAmount == "" { 131 | 132 | printUsage() 133 | os.Exit(1) 134 | } 135 | 136 | //cli.addBlock(*flagAddBlockData) 137 | 138 | //这里真正地调用转账方法 139 | //fmt.Println(*flagSendBlockFrom) 140 | //fmt.Println(*flagSendBlockTo) 141 | //fmt.Println(*flagSendBlockAmount) 142 | // 143 | //fmt.Println(Json2Array(*flagSendBlockFrom)) 144 | //fmt.Println(Json2Array(*flagSendBlockTo)) 145 | //fmt.Println(Json2Array(*flagSendBlockAmount)) 146 | cli.send( 147 | Json2Array(*flagSendBlockFrom), 148 | Json2Array(*flagSendBlockTo), 149 | Json2Array(*flagSendBlockAmount), 150 | ) 151 | } 152 | //对printchainCmd命令的解析 153 | if printchainCmd.Parsed() { 154 | 155 | cli.printchain() 156 | } 157 | // 158 | if createBlockchainCmd.Parsed() { 159 | 160 | if *flagCreateBlockchainAddress == "" { 161 | 162 | cli.creatBlockchain(*flagCreateBlockchainAddress) 163 | } 164 | 165 | cli.creatBlockchain(*flagCreateBlockchainAddress) 166 | } 167 | 168 | if blanceBlockCmd.Parsed() { 169 | 170 | if *flagBlanceBlockAddress == "" { 171 | 172 | printUsage() 173 | os.Exit(1) 174 | } 175 | 176 | cli.getBlance(*flagBlanceBlockAddress) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /part8-transfer-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | "encoding/json" 19 | ) 20 | 21 | //将int64转换为bytes 22 | func IntToHex(num int64) []byte { 23 | 24 | buff := new(bytes.Buffer) 25 | err := binary.Write(buff, binary.BigEndian, num) 26 | if err != nil { 27 | 28 | log.Panic(err) 29 | } 30 | 31 | return buff.Bytes() 32 | } 33 | 34 | // 标准的JSON字符串转数组 35 | func Json2Array(jsonString string) []string { 36 | 37 | //json 到 []string 38 | var sArr []string 39 | if err := json.Unmarshal([]byte(jsonString), &sArr); err != nil { 40 | 41 | log.Panic(err) 42 | } 43 | return sArr 44 | } -------------------------------------------------------------------------------- /part8-transfer-Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part8-transfer-Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part8-transfer-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part8-transfer-Prototype/main -------------------------------------------------------------------------------- /part8-transfer-Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part8-transfer-Prototype/BLC" 6 | //"fmt" 7 | //"os" 8 | //"flag" 9 | //"log" 10 | //"fmt" 11 | ) 12 | 13 | func main() { 14 | 15 | cli := BLC.CLI{} 16 | cli.Run() 17 | 18 | //blc := BLC.CreateBlockchainWithGensisBlock("chaors") 19 | //utxos := blc.UnUTXOs("chaors") 20 | //fmt.Println(utxos) 21 | } 22 | -------------------------------------------------------------------------------- /part8-transfer-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 转账 3 | 1.cli addBlock -- > send 4 | 2.json2Arr 5 | 3.转账方法MineNewBlock 6 | 4.改造AddBlock -->mine 7 | 5.Transaction +newTRansaction 新普通交易 8 | 6.unSpentTx 9 | 7.cli getbalance 10 | 8.utxo 11 | **/ -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/Block.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: Block.go 5 | 6 | @time: 2018/06/21 21:46 7 | 8 | @desc: 区块信息的基础结构 9 | */ 10 | 11 | package BLC 12 | 13 | import ( 14 | "time" 15 | "bytes" 16 | "encoding/gob" 17 | "log" 18 | "fmt" 19 | "crypto/sha256" 20 | ) 21 | 22 | type Block struct { 23 | //1.区块高度 24 | Height int64 25 | //2.上一个区块HAsh 26 | PrevBlockHash []byte 27 | //3.交易数据 28 | Txs [] *Transaction 29 | //4.时间戳 30 | Timestamp int64 31 | //5.Hash 32 | Hash []byte 33 | //6.Nonce 符合工作量证明的随机数 34 | Nonce int64 35 | } 36 | 37 | //区块序列化 38 | func (block *Block) Serialize() []byte { 39 | 40 | var result bytes.Buffer 41 | 42 | encoder := gob.NewEncoder(&result) 43 | 44 | err := encoder.Encode(block) 45 | if err != nil { 46 | log.Panic(err) 47 | } 48 | 49 | return result.Bytes() 50 | } 51 | 52 | //区块反序列化 53 | func DeSerializeBlock(blockBytes []byte) *Block { 54 | 55 | var block Block 56 | 57 | decoder := gob.NewDecoder(bytes.NewReader(blockBytes)) 58 | 59 | err := decoder.Decode(&block) 60 | if err != nil { 61 | log.Panic(err) 62 | } 63 | 64 | return &block 65 | } 66 | 67 | 68 | //1.创建新的区块 69 | func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block { 70 | 71 | //创建区块 72 | block := &Block{ 73 | Height: height, 74 | PrevBlockHash: prevBlockHash, 75 | Txs: txs, 76 | Timestamp: time.Now().Unix(), 77 | Hash: nil, 78 | Nonce: 0} 79 | 80 | //调用工作量证明返回有效的Hash 81 | pow := NewProofOfWork(block) 82 | hash, nonce := pow.Run() 83 | block.Hash = hash[:] 84 | block.Nonce = nonce 85 | 86 | fmt.Printf("\r######%d-%x\n", nonce, hash) 87 | 88 | return block 89 | } 90 | 91 | //单独方法生成创世区块 92 | func CreateGenesisBlock(txs []*Transaction) *Block { 93 | 94 | return NewBlock( 95 | txs, 96 | 1, 97 | []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 98 | ) 99 | } 100 | 101 | // 需要将Txs转换成[]byte 102 | func (block *Block) HashTransactions() []byte { 103 | 104 | var txHashes [][]byte 105 | var txHash [32]byte 106 | 107 | for _, tx := range block.Txs { 108 | 109 | txHashes = append(txHashes, tx.TxHAsh) 110 | } 111 | txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) 112 | 113 | return txHash[:] 114 | 115 | } 116 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/BlockchainIterator.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "github.com/boltdb/bolt" 5 | "log" 6 | ) 7 | 8 | //区块链迭代器 9 | type BlockchainIterator struct { 10 | //当前遍历hash 11 | CurrHash []byte 12 | //区块链数据库 13 | DB *bolt.DB 14 | } 15 | 16 | func (blcIterator *BlockchainIterator) Next() *Block { 17 | 18 | var block *Block 19 | 20 | err := blcIterator.DB.View(func(tx *bolt.Tx) error{ 21 | 22 | b := tx.Bucket([]byte(blockTableName)) 23 | 24 | if b != nil { 25 | currentBloclBytes := b.Get(blcIterator.CurrHash) 26 | 27 | // 获取到当前迭代器里面的currentHash所对应的区块 28 | block = DeSerializeBlock(currentBloclBytes) 29 | 30 | // 更新迭代器里面CurrentHash 31 | blcIterator.CurrHash = block.PrevBlockHash 32 | } 33 | 34 | return nil 35 | }) 36 | if err != nil { 37 | log.Panic(err) 38 | } 39 | 40 | return block 41 | } 42 | 43 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/CLI.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "fmt" 5 | "flag" 6 | "os" 7 | "log" 8 | ) 9 | 10 | type CLI struct { 11 | 12 | } 13 | 14 | //打印目前左右命令使用方法 15 | func printUsage() { 16 | fmt.Println("Usage:") 17 | fmt.Println("\tcreateBlockchain -address --创世区块地址 ") 18 | fmt.Println("\tsend -from FROM -to TO -amount AMOUNT --交易明细") 19 | fmt.Println("\tprintchain --打印所有区块信息") 20 | fmt.Println("\tgetbalance -address -- 输出区块信息.") 21 | } 22 | 23 | func isValidArgs() { 24 | 25 | //获取当前输入参数个数 26 | if len(os.Args) < 2 { 27 | printUsage() 28 | os.Exit(1) 29 | } 30 | } 31 | 32 | func (cli *CLI) Run() { 33 | 34 | isValidArgs() 35 | 36 | //自定义cli命令 37 | sendBlockCmd := flag.NewFlagSet("send", flag.ExitOnError) 38 | printchainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) 39 | createBlockchainCmd := flag.NewFlagSet("createBlockchain", flag.ExitOnError) 40 | blanceBlockCmd := flag.NewFlagSet("getBalance", flag.ExitOnError) 41 | 42 | //addBlockCmd 设置默认参数 43 | flagSendBlockFrom := sendBlockCmd.String("from", "", "源地址") 44 | flagSendBlockTo := sendBlockCmd.String("to", "", "目标地址") 45 | flagSendBlockAmount := sendBlockCmd.String("amount", "", "转账金额") 46 | flagCreateBlockchainAddress := createBlockchainCmd.String("address", "", "创世区块地址") 47 | flagBlanceBlockAddress := blanceBlockCmd.String("address", "", "输出区块信息") 48 | 49 | //解析输入的第二个参数是addBlock还是printchain,第一个参数为./main 50 | switch os.Args[1] { 51 | case "send": 52 | //第二个参数为相应命令,取第三个参数开始作为参数并解析 53 | err := sendBlockCmd.Parse(os.Args[2:]) 54 | if err != nil { 55 | log.Panic(err) 56 | } 57 | case "printchain": 58 | err := printchainCmd.Parse(os.Args[2:]) 59 | if err != nil { 60 | log.Panic(err) 61 | } 62 | case "createBlockchain": 63 | err := createBlockchainCmd.Parse(os.Args[2:]) 64 | if err != nil { 65 | log.Panic(err) 66 | } 67 | case "getBalance": 68 | err := blanceBlockCmd.Parse(os.Args[2:]) 69 | if err != nil { 70 | log.Panic(err) 71 | } 72 | default: 73 | printUsage() 74 | os.Exit(1) 75 | } 76 | 77 | //对addBlockCmd命令的解析 78 | if sendBlockCmd.Parsed() { 79 | 80 | if *flagSendBlockFrom == "" { 81 | 82 | printUsage() 83 | os.Exit(1) 84 | } 85 | if *flagSendBlockTo == "" { 86 | 87 | printUsage() 88 | os.Exit(1) 89 | } 90 | if *flagSendBlockAmount == "" { 91 | 92 | printUsage() 93 | os.Exit(1) 94 | } 95 | 96 | //cli.addBlock(*flagAddBlockData) 97 | 98 | //这里真正地调用转账方法 99 | //fmt.Println(*flagSendBlockFrom) 100 | //fmt.Println(*flagSendBlockTo) 101 | //fmt.Println(*flagSendBlockAmount) 102 | // 103 | //fmt.Println(Json2Array(*flagSendBlockFrom)) 104 | //fmt.Println(Json2Array(*flagSendBlockTo)) 105 | //fmt.Println(Json2Array(*flagSendBlockAmount)) 106 | cli.send( 107 | Json2Array(*flagSendBlockFrom), 108 | Json2Array(*flagSendBlockTo), 109 | Json2Array(*flagSendBlockAmount), 110 | ) 111 | } 112 | //对printchainCmd命令的解析 113 | if printchainCmd.Parsed() { 114 | 115 | cli.printchain() 116 | } 117 | // 118 | if createBlockchainCmd.Parsed() { 119 | 120 | if *flagCreateBlockchainAddress == "" { 121 | 122 | cli.creatBlockchain(*flagCreateBlockchainAddress) 123 | } 124 | 125 | cli.creatBlockchain(*flagCreateBlockchainAddress) 126 | } 127 | 128 | if blanceBlockCmd.Parsed() { 129 | 130 | if *flagBlanceBlockAddress == "" { 131 | 132 | printUsage() 133 | os.Exit(1) 134 | } 135 | 136 | cli.getBlance(*flagBlanceBlockAddress) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/CLI_creatBlockchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //新建区块链 6 | func (cli *CLI)creatBlockchain(address string) { 7 | 8 | blockchain := CreateBlockchainWithGensisBlock(address) 9 | defer blockchain.DB.Close() 10 | } 11 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/CLI_getBlance.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import "fmt" 4 | 5 | 6 | //查询余额 7 | func (cli *CLI) getBlance(address string) { 8 | 9 | fmt.Println("地址:" + address) 10 | 11 | blockchain := GetBlockchain() 12 | defer blockchain.DB.Close() 13 | 14 | amount := blockchain.GetBalance(address) 15 | 16 | fmt.Printf("%s一共有%d个Token\n", address, amount) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/CLI_printchain.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | //打印区块链 5 | func (cli *CLI) printchain() { 6 | 7 | blockchain := GetBlockchain() 8 | defer blockchain.DB.Close() 9 | 10 | blockchain.Printchain() 11 | } 12 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/CLI_send.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | 4 | 5 | //转账 6 | func (cli *CLI) send(from []string, to []string, amount []string) { 7 | 8 | blockchain := GetBlockchain() 9 | defer blockchain.DB.Close() 10 | 11 | blockchain.MineNewBlock(from, to, amount) 12 | } -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/ProofOfwork.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | import ( 4 | "math/big" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | ) 9 | 10 | //期望计算的Hash值前面至少要有16个零 11 | const targetBits = 18 12 | 13 | type ProofOfWork struct { 14 | //求工作量的block 15 | Block *Block 16 | //工作量难度 big.Int大数存储 17 | target *big.Int 18 | } 19 | 20 | //创建新的工作量证明对象 21 | func NewProofOfWork(block *Block) *ProofOfWork { 22 | 23 | /** 24 | target计算方式 假设:Hash为8位,targetBit为2位 25 | eg:0000 0001(8位的Hash) 26 | 1.8-2 = 6 将上值左移6位 27 | 2.0000 0001 << 6 = 0100 0000 = target 28 | 3.只要计算的Hash满足 :hash < target,便是符合POW的哈希值 29 | */ 30 | 31 | //1.创建一个初始值为1的target 32 | target := big.NewInt(1) 33 | //2.左移bits(Hash) - targetBit 位 34 | target = target.Lsh(target, 256-targetBits) 35 | 36 | return &ProofOfWork{block, target} 37 | } 38 | 39 | //拼接区块属性,返回字节数组 40 | func (pow *ProofOfWork) prepareData(nonce int) []byte { 41 | 42 | data := bytes.Join( 43 | [][]byte{ 44 | pow.Block.PrevBlockHash, 45 | pow.Block.HashTransactions(), 46 | IntToHex(pow.Block.Timestamp), 47 | IntToHex(int64(targetBits)), 48 | IntToHex(int64(nonce)), 49 | IntToHex(int64(pow.Block.Height)), 50 | }, 51 | []byte{}, 52 | ) 53 | 54 | return data 55 | } 56 | 57 | //判断当前区块是否有效 58 | func (proofOfWork *ProofOfWork) IsValid() bool { 59 | 60 | //比较当前区块哈希值与目标哈希值 61 | var hashInt big.Int 62 | hashInt.SetBytes(proofOfWork.Block.Hash) 63 | 64 | if proofOfWork.target.Cmp(&hashInt) == 1 { 65 | 66 | return true 67 | } 68 | 69 | return false 70 | } 71 | 72 | //运行工作量证明 73 | func (proofOfWork *ProofOfWork) Run() ([]byte, int64) { 74 | 75 | //1.将Block属性拼接成字节数组 76 | 77 | //2.生成hash 78 | //3.判断Hash值有效性,如果满足条件跳出循环 79 | 80 | //用于寻找目标hash值的随机数 81 | nonce := 0 82 | //存储新生成的Hash值 83 | var hashInt big.Int 84 | var hash [32]byte 85 | 86 | fmt.Println("正在挖矿...") 87 | 88 | for { 89 | //准备数据 90 | dataBytes := proofOfWork.prepareData(nonce) 91 | //生成Hash 92 | hash = sha256.Sum256(dataBytes) 93 | 94 | //\r将当前打印行覆盖 95 | fmt.Printf("\r%x", hash) 96 | //存储Hash到hashInt 97 | hashInt.SetBytes(hash[:]) 98 | //验证Hash 99 | if proofOfWork.target.Cmp(&hashInt) == 1 { 100 | 101 | break 102 | } 103 | nonce++ 104 | } 105 | 106 | return hash[:], int64(nonce) 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/TXInput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type TXInput struct { 4 | //交易ID 5 | TxHash []byte 6 | //存储TXOutput在Vouts里的索引 7 | Vout int 8 | //数字签名 9 | ScriptSig string 10 | } 11 | 12 | //验证当前输入是否是当前地址的 13 | func (txInput *TXInput) UnlockWithAddress(address string) bool { 14 | 15 | return txInput.ScriptSig == address 16 | } -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/TXOutput.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type TXOutput struct { 4 | //面值 5 | Value int64 6 | //暂时理解为用户名 7 | ScriptPubKey string 8 | } 9 | 10 | //验证当前交易输出属于某用户 11 | func (txOutput *TXOutput) UnLockScriptPubKeyWithAddress(address string) bool { 12 | 13 | return txOutput.ScriptPubKey == address 14 | } 15 | 16 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/UTXO.go: -------------------------------------------------------------------------------- 1 | package BLC 2 | 3 | type UTXO struct { 4 | //来自交易的哈希 5 | TxHash []byte 6 | //在该交易VOuts里的下标 7 | Index int 8 | //未花费的交易输出 9 | Output *TXOutput 10 | } 11 | 12 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/BLC/utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author: chaors 3 | 4 | @file: utils.go 5 | 6 | @time: 2018/06/21 22:06 7 | 8 | @desc: 一些常用的辅助方法 9 | */ 10 | 11 | 12 | package BLC 13 | 14 | import ( 15 | "bytes" 16 | "encoding/binary" 17 | "log" 18 | "encoding/json" 19 | ) 20 | 21 | //将int64转换为bytes 22 | func IntToHex(num int64) []byte { 23 | 24 | buff := new(bytes.Buffer) 25 | err := binary.Write(buff, binary.BigEndian, num) 26 | if err != nil { 27 | 28 | log.Panic(err) 29 | } 30 | 31 | return buff.Bytes() 32 | } 33 | 34 | // 标准的JSON字符串转数组 35 | func Json2Array(jsonString string) []string { 36 | 37 | //json 到 []string 38 | var sArr []string 39 | if err := json.Unmarshal([]byte(jsonString), &sArr); err != nil { 40 | 41 | log.Panic(err) 42 | } 43 | return sArr 44 | } -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/chaorsBlockchain.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part9-transfer_1-Prototype/chaorsBlockchain.db -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaors/PublicBlockChain_go/31f4467e78e25c769ec5f162b0c0f62ce2a86cb4/part9-transfer_1-Prototype/main -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | 5 | "chaors.com/LearnGo/publicChaorsChain/part9-transfer_1-Prototype/BLC" 6 | ) 7 | 8 | func main() { 9 | 10 | cli := BLC.CLI{} 11 | cli.Run() 12 | 13 | //blc := BLC.CreateBlockchainWithGensisBlock("chaors") 14 | //utxos := blc.UnUTXOs("chaors") 15 | //fmt.Println(utxos) 16 | } 17 | -------------------------------------------------------------------------------- /part9-transfer_1-Prototype/readMe: -------------------------------------------------------------------------------- 1 | /* 2 | 转账 3 | 1.UTXO结构 4 | 2.改造UTXOs函数 5 | 3.cli优化--文件分离 6 | 4.FindSpendableUTXOs 7 | 5.NewTransaction 8 | 6.utxos函数方法 计算余额错误 9 | 7.MineNewBlock处理多笔交易 多笔交易处理第二笔交易时,第一笔交易已经把数据库数据改变了 10 | 所以获取utxo还需要txs参数 11 | **/ --------------------------------------------------------------------------------