区块链One——创建简单的区块链/工作量证明
这是实习的第三天了吧,感觉只是说对go稍有了解,代码可以看懂。找了教程说什么以太坊,区块链完全不太明白。但是今天在简书: liuchengxu 看到了关于区块链的构建,简单记录一下。也不知道我的这个学习顺序对不对,看完语言方面直接跳过以太坊来到区块链。小白笔记,是笔记!!!有问题请大佬告知!!!
创建简单的区块链
1.像java一样先来了一个结构体(java里面是实体类)
type Block struct {
Timestamp int64 //当前的时间戳(区块的创建时间)
Data []byte //区块存储的有效信息
PrevBlockHash []byte //前一个区块的哈希(key-value)
Hash []byte //当前区块的哈希
}
正常情况下(比特币技术),区块头单独数据结构,交易(Data)是另一个单独的数据结构。这个简单例子放在一起。
2.计算哈希(区块链非常重要的部分)
func (b *Block) SetHash() {
timestamp := []byte(strconv.FormatInt(b.Timestamp, 10)) //赋值
/*
(1)[]byte:它与string一样底层为数组,赋值方法不同,具体的不同点请参考:https://www.cnblogs.com/zhangboyu/p/7623712.html
(2)strconv包:FormatInt方法将int值转换为字符串true或false。第一个参数表示获取结构体中的时间戳;第二个参数表示时间戳返回几进制,这里是十进制。
*/
headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})
/*
bytes包:Join方法用字节切片PrevBlockHash、Data、timestamp链接 成一个字节切片并返回,有关bytes的其他方法:https://blog.csdn.net/qq_32340877/article/details/78952486
*/
hash := sha256.Sum256(headers)
/*
SHA256:需要import "crypto/sha256",其意义就是进行一个加密
*/
b.Hash = hash[:] //设置哈希
}
3.构造创建区块的函数
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}} //取得这个指针对象初始化之后的地址。第一个参数以秒为单位
block.SetHash() //调用上面的方法
return block
}
4.实现区块链
(1)创建一个区块
可以通过array(存储有序哈希)和map实现(存储无序的hask->block)
type Blockchain struct {
blocks []*Block
}
(2)创建创世区块
加入新区快的前提是有一个区块,这样链才不会空
func NewGenesisBlock() *Block {
return NewBlock("Genesis Block", []byte{})
}
(3)添加区块
func (bc *Blockchain) AddBlock(data string) {
prevBlock := bc.blocks[len(bc.blocks)-1] //取出最后一个区块
newBlock := NewBlock(data, prevBlock.Hash) //创建一个区块
bc.blocks = append(bc.blocks, newBlock) //插入新的区块
}
(5)创建区块链(有创世块的)
func NewBlockchain() *Blockchain {
return &Blockchain{[]*Block{NewGenesisBlock()}}
}
5.使用区块链
func main() {
bc := NewBlockchain() //创建区块链
bc.AddBlock("Send 1 BTC to Ivan") //增加区块
bc.AddBlock("Send 2 more BTC to Ivan") //增加区块
for _, block := range bc.blocks { //遍历区块输出
fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Hash: %x\n", block.Hash)
fmt.Println()
}
}
工作量证明
上面所创建的区块链,加入新的区块太容易。但是区块链要完成一些困难的工作才能加入一个新的区块,完成困难工作就是常说的挖矿(在比特币中通过我们不断的加入新区块获得比特币)。完成工作之后需要证明你完成了,这整个过程(努力工作到证明)叫做工作量证明
那我们具体的工作是什么呢?在比特币中是通过大量的计算找出一块有效的哈希就是我们实际要做的
1.定义挖矿难度
在比特币中,targetbits代表区块头存储的难度,这里24代表了算出来的哈希前24位必须为0
const targetBits = 24 //不实现动态调整难度,所以这里先用常量
2.构造ProofOfWork 结构
type ProofOfWork struct {
block *Block //指向一个块的指针
target *big.Int //指向一个目标的指针(存储计算哈希对比的特定整数)
}
3.创建工作量证明的挖矿对象
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1) //初始化目标整数
target.Lsh(target, uint(256-targetBits)) //数据转换:将target左移256 - targetBits位,也就是2^(256-targetbits)次方,其中256是SHA-256 哈希的位数
pow := &ProofOfWork{b, target}
return pow
}
4.准备数据
func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
pow.block.PrevBlockHash, //上一块哈希
pow.block.Data, //当前的数据
IntToHex(pow.block.Timestamp), //时间(十六进制)
IntToHex(int64(targetBits)), //位数(十六进制)
IntToHex(int64(nonce)), //保存工作量的none(哈希计算中的计数器)
},
[]byte{},
)
return data
}
5.PoW算法核心的实现(挖矿执行)
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0 //计数器
fmt.Printf("当前挖矿计算的区块数据 \"%s\"\n", pow.block.Data)
for nonce < maxNonce { //对这个挖矿过程进行限制,防止nonce溢出(可能性很小)
data := pow.prepareData(nonce) //准备好的数据
hash = sha256.Sum256(data) //计算出哈希
hashInt.SetBytes(hash[:]) //将hash换成大整数(用来对比的数据)
if hashInt.Cmp(pow.target) == -1 { //挖矿校验
fmt.Printf("\r%x", hash)
break
} else {
nonce++
}
}
fmt.Print("\n\n")
return nonce, hash[:] //nonce为解题的答案,hash是当前的哈希
}
6.区块的结构体
type Block struct {
Timestamp int64 //时间线
Data []byte //交易数据
PrevBlockHash []byte //上一块数据的哈希
Hash []byte //当前块数据的哈希
Nonce int //工作量证明
}
7.构造创建区块的方法
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0} //取得这个指针对象初始化之后的地址。第一个参数以秒为单位
pow := NewProofOfWork(block) //挖矿附加这个区块
nonce, hash := pow.Run() //开始挖矿
block.Hash = hash[:]
block.Nonce = nonce
return block
}
8.工作量证明(挖矿是否成功)
func (pow *ProofOfWork) Validate() bool {
var hashInt big.Int
data := pow.prepareData(pow.block.Nonce) //准备好的数据
hash := sha256.Sum256(data) //计算哈希
hashInt.SetBytes(hash[:]) //将hash换成大整数(用来对比的数据)
isValid := hashInt.Cmp(pow.target) == -1 //校验数据
return isValid
}
9.主函数下来执行上述方法(main.go)
fmt.Println("hello game start")
bc:=NewBlockchain() //创建区块链
bc.AddBlock("a1 10")
bc.AddBlock("a2 20")
bc.AddBlock("a3 pay 小红 30")
for_,block:=rangebc.blocks{
fmt.Printf("上一块哈希%x\n",block.PrevBlockHash)
fmt.Printf("数据: %s\n",block.Data)
fmt.Printf("当前哈希%x\n",block.Hash)
pow:=NewProofOfWork(block) //校验工作量
fmt.Printf("pow %s\n",strconv.FormatBool(pow.Validate()))
fmt.Println()
}