博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从第一行代码开始开发区块链(二)
阅读量:6152 次
发布时间:2019-06-21

本文共 5061 字,大约阅读时间需要 16 分钟。

1240

传送门:


如何通过go语言打造区块链

为什么选择go语言呢?因为个人兴趣爱好,作为后端语言go确实比c++要舒服一些,此外go语言对加密算法,hash函数支持的也非常好。

我们要支持哪些功能?

  • 有区块的链表
  • pow 共识机制
  • UTXO模型

1. 有区块的链表

go语言里借助数组或切片就可以模拟有序链表,所以直接用切片即可,一个区块包含哪些信息呢?

type Block struct {    Timestamp     int64  // 时间戳 类似 1546590891    Data          []byte // 打包的交易数据,我们可以随意模拟    PrevBlockHash []byte // 前一块hash值    Hash          []byte // 当前hash值}

那么hash如何计算呢?可以把前一块hash,本块数据,时间戳等进行加工(join)计算,最后借助sha256包的sum256函数获得本块hash值。

timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))    headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})    hash := sha256.Sum256(headers)

上述代码中 strconv.FormatInt(b.Timestamp, 10) 就是将时间戳转换为字符串,最后通过[]byte强制转换为[]byte类型。

type Blockchain struct {    blocks []*Block}

上述定义就代表我们有了一个区块链,是不是太简单了!接下来编写如何添加区块!

func (bc *Blockchain) AddBlock(data string) {    prevBlock := bc.blocks[len(bc.blocks)-1]    newBlock := NewBlock(data, prevBlock.Hash)    bc.blocks = append(bc.blocks, newBlock)}

我们这样测试,并且打印区块链结果

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()    }}

ok,到这里我们完成了一个最初版本的开发。

全部代码如下:

  • main.go
package mainimport (    "fmt")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()    }}
  • block.go
package mainimport (    "bytes"    "crypto/sha256"    "strconv"    "time")// Block keeps block headerstype Block struct {    Timestamp     int64    Data          []byte    PrevBlockHash []byte    Hash          []byte}// SetHash calculates and sets block hashfunc (b *Block) SetHash() {    timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))    headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})    hash := sha256.Sum256(headers)    b.Hash = hash[:]}// NewBlock creates and returns Blockfunc NewBlock(data string, prevBlockHash []byte) *Block {    block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}    block.SetHash()    return block}// NewGenesisBlock creates and returns genesis Blockfunc NewGenesisBlock() *Block {    return NewBlock("Genesis Block", []byte{})}
  • blockchain.go
package main// Blockchain keeps a sequence of Blockstype Blockchain struct {    blocks []*Block}// AddBlock saves provided data as a block in the blockchainfunc (bc *Blockchain) AddBlock(data string) {    prevBlock := bc.blocks[len(bc.blocks)-1]    newBlock := NewBlock(data, prevBlock.Hash)    bc.blocks = append(bc.blocks, newBlock)}// NewBlockchain creates a new Blockchain with genesis Blockfunc NewBlockchain() *Blockchain {    return &Blockchain{[]*Block{NewGenesisBlock()}}}

2. 产生区块的工作量证明

之前我们的代码已经可以生成区块了,但是产生区块太容易了,这样任何人都可以添加一个区块,给记账也带来混乱。前面我们也提到了,想要在链表中增加区块,那么必须做一个数学难题,这个难题就是找到一个适合的数字能让它产生一个符合条件的hash值,而且这个hash值必须小于某个数。计算出这个hash值对应的数字没有投机取巧的办法,只能自己尝试,谁先拿到了,谁就中奖了。当然,这个事儿可以靠计算能力来作弊,举个简单的例子,假设一共有256个值需要尝试,你的计算能力如果是别人的4倍,比如你有四台机器同时计算,那么你中奖的机会也就是别人的4倍。下面还是来说代码,需要对之前的代码改造。

在产生区块时需要经过一个hash计算,而且这个值必须小于一个数,习惯上把它成为挖矿难度。

var (    maxNonce = math.MaxInt64)const targetBits = 24// ProofOfWork represents a proof-of-worktype ProofOfWork struct {    block  *Block    target *big.Int}

math.MaxInt64 实际上是1左移63位后-1

MaxInt64  = 1<<63 - 1

targetBits 实际上就是挖矿难度了,需要通过这个挖矿难度最后再计算出一个数。

接下来我们实现生成ProofOfWork结构体的函数

func NewProofOfWork(b *Block) *ProofOfWork {    target := big.NewInt(1)    target.Lsh(target, uint(256-targetBits))    fmt.Println("target======", target)    pow := &ProofOfWork{b, target}    return pow}

将前一块的内容加上本块数据结合起来,准备去挖矿

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)),        },        []byte{},    )    return data}

循环实验,也就是挖矿,hashInt.Cmp 是如果hashInt小于target则返回-1,这样就ok了。

func (pow *ProofOfWork) Run() (int, []byte) {    var hashInt big.Int    var hash [32]byte    nonce := 0    fmt.Printf("Mining the block containing \"%s\"maxNonce=%d\n", pow.block.Data, maxNonce)    for nonce < maxNonce {        data := pow.prepareData(nonce)        hash = sha256.Sum256(data)        fmt.Printf("\r%x", hash)        hashInt.SetBytes(hash[:])        if hashInt.Cmp(pow.target) == -1 {            break        } else {            nonce++        }    }    fmt.Print("\n\n")    return nonce, hash[:]}

顺便我们再增加一个验证的函数,验证是否挖到矿

func (pow *ProofOfWork) Validate() bool {    var hashInt big.Int    data := pow.prepareData(pow.block.Nonce)    hash := sha256.Sum256(data)    hashInt.SetBytes(hash[:])    isValid := hashInt.Cmp(pow.target) == -1    return isValid}


%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20190103092604.jpg

转载于:https://www.cnblogs.com/tokenpai/p/10478389.html

你可能感兴趣的文章
Android JSON数据解析
查看>>
DEV实现日期时间效果
查看>>
java注解【转】
查看>>
centos 下安装g++
查看>>
嵌入式,代码调试----GDB扫盲
查看>>
下一步工作分配
查看>>
Response. AppendHeader使用大全及文件下载.net函数使用注意点(转载)
查看>>
Wait Functions
查看>>
jQuery最佳实践
查看>>
centos64i386下apache 403没有权限访问。
查看>>
jquery用法大全
查看>>
PC-BSD 9.2 发布,基于 FreeBSD 9.2
查看>>
css斜线
查看>>
Windows phone 8 学习笔记(3) 通信
查看>>
Revit API找到风管穿过的墙(当前文档和链接文档)
查看>>
Scroll Depth – 衡量页面滚动的 Google 分析插件
查看>>
Windows 8.1 应用再出发 - 视图状态的更新
查看>>
自己制作交叉编译工具链
查看>>
Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
查看>>
[物理学与PDEs]第3章习题1 只有一个非零分量的磁场
查看>>