├── .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 | 
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 | 
9 |
10 | 那么MerkleTree对我们构造公链有什么用呢?
11 |
12 | 我们知道完整的比特币数据库已达到一百多Gb的存储,对于每一个节点必须保存一个区块链的完整副本。这对于大多数使用比特币的人显然不合适,于是中本聪提出了简单支付验证SPV(Simplified Payment Verification).
13 |
14 | 简单地说,SPV是一个轻量级的比特币节点,它并不需要下载区块链的所有数据内容。为了实现SPV,就需要有一个方式来检查某区块是否包含某一笔交易,这就是MerkleTree能帮我们解决的问题。
15 |
16 | 一个区块的结构里只有一个哈希值,但是这个哈希值包含了所有交易的哈希值。我们将区块内所有交易哈希的值两两进行哈希得到一个新的哈希值,然后再把得到的新的哈希值两两哈希...不断进行这个过程直到最后只存在一个哈希值。这样的结构是不是很像一颗二叉树,我们将这样的二叉树就叫做MerkleTree。
17 |
18 | 比特币的MerkleTree结构图:
19 |
20 | 
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 |
5 |
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 | 
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 | **/
--------------------------------------------------------------------------------