继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

go语言创建区块链

ruibin
关注TA
已关注
手记 77
粉丝 9109
获赞 2572

区块链是借由密码学[1][6]串接并保护内容的串连交易记录(又称区块)。每一个区块包含了前一个区块的加密散列、相应时间戳记以及交易数据(通常用默克尔树算法计算的散列值表示)(wiki)


从技术角度主要包括,区块、区块组成的区块链、分布式数据储存。

区块中包含交易信息,有交易就需要记账。区块有一个很重要的特性是能创建和查询,但不能修改。

区块链中包含一个一个的区块,通过当前区块hash和上个区块hash将一个一个区块串联起来。

分布式数据存储包含共识机制和钱包,共识机制一般采用工作量证明的方法去实现,钱包一般是一组公钥和私钥,这里面就涉及到加密技术。

我们常听说的挖矿,就是工作量证明的一种方式,浪费电力和时间计算一组没用的数字(这个数字相对是一个比较稀缺的数字,是通过算法来产生的,并且一定时间会增加难度,例如开始时只需要最开始有一个0的hash,难度增加要求前面必须是有10个0的hash值,然后100个0等等~)。

这篇文章只是一个入门篇,这里只分享创建区块和区块链,以及简单的增加查询操作,实现起来非常简单。

block.go

package core

import (
   "crypto/sha256"
   "encoding/hex"
   "time"
   )

type Block struct {
   Index int64
   Hash string
   PHash string
   Data string
   Time int64
}

func CalcHash(data string) string {
   hash := sha256.Sum256([]byte(data))
   return hex.EncodeToString(hash[:])
}
func (block Block) GenerationNewBlock(data string, pBlock *Block) Block  {
   newBlock := Block{}
   newBlock.Index = pBlock.Index + 1
   newBlock.PHash = pBlock.Hash
   newBlock.Data = data
   newBlock.Time = time.Now().Unix()
   hashStr := string(newBlock.Index) + string(newBlock.Time) + newBlock.PHash + newBlock.Data
   newBlock.Hash = CalcHash(hashStr)
   return newBlock
}

func (block Block) GenerationFirstBlock(data string) Block {
   firstBlock := Block{}
   firstBlock.Index = 0
   firstBlock.PHash = ""
   firstBlock.Data = data
   firstBlock.Time = time.Now().Unix()
   hashStr := string(firstBlock.Index) + string(firstBlock.Time) + firstBlock.PHash + firstBlock.Data
   firstBlock.Hash = CalcHash(hashStr)
   return firstBlock
}

chainBlock.go

package core

import (
   "fmt"
   "log"
)

type ChainBlock struct {
   Blocks []*Block
}

func (chain *ChainBlock) AppendNewBlock(newBlock *Block) *ChainBlock  {
   if newBlock.Index == 0 {
      chain.Blocks = append(chain.Blocks, newBlock)
      return chain
   }
   currBlock := chain.Blocks[len(chain.Blocks) - 1]
   if chain.IsValid(newBlock, currBlock) {
      chain.Blocks = append(chain.Blocks, newBlock)
   } else {
      log.Fatal("unvalid block")
   }
   return chain

}

func (chain *ChainBlock) IsValid(newBlock, pBlock *Block) bool  {
   if newBlock.PHash != pBlock.Hash {
      return false
   }
   if newBlock.Index - 1 != pBlock.Index {
      return false
   }
   if newBlock.GenerationNewBlock(newBlock.Data, pBlock).Hash != newBlock.Hash {
      return false
   }
   return true
}

func (chain *ChainBlock) AppendStringToChain(data string)  {
   block := Block{}
   if len(chain.Blocks) == 0 {
      block = block.GenerationFirstBlock(data)
      chain.AppendNewBlock(&block)
      return
   }
   block = block.GenerationNewBlock(data, chain.Blocks[len(chain.Blocks) - 1])
   chain.AppendNewBlock(&block)

}
func (chain *ChainBlock) Print() {
   for _, item := range chain.Blocks {
      fmt.Println("Index: ", item.Index)
      fmt.Println("Hash: ", item.Hash)
      fmt.Println("PHash: ", item.PHash)
      fmt.Println("Data: ", item.Data)
      fmt.Println("Time: ", item.Time)
      fmt.Println()

   }
   fmt.Println(chain.Blocks)
}

main.go

package main

import "core"

func main() {
   chain := core.ChainBlock{}
   chain.AppendStringToChain("hello")
   chain.AppendStringToChain("world")
   chain.Print()
}

这里有两点需要注意的地方:

1、找不到core包问题,这里需要设置go语言的goPath为当前的目录,如果使用的是goland这款软件,具体路径为file ->go -> GOPATH。

2、代码里面很多函数和方法都是使用指针的方式传参,这里最主要的原因是需要修改原数据,而结构体struct本身是个基本数据类型,传递参数的时候是传值的方式,如果想要修改原数据,就必须传递指针。


代码还比较粗糙,权当看下,了解下。下面科普下go语言值方法和指针方法遵循的规则

1、接收者变量的值实际上是源值的一个复制品。如果这个值不是指针类型的,那么在值方法中就没有途径去改变其源值。而指针值和其复制品指向的是同一个值,所以在指针方法中就存在改变源值的途径。这里有一个例外,如果接收者类型是一个引用类型或者他的别名类型,那么即使是值方法,也可以改变其源值类型。

2、对于某个非指针的数据类型,与他关联的方法集合中只包含它的值方法。而对于指针类型,其方法集合中既包含其值方法,也包含其指针方法。不过,在非指针数据类型的值上,也是能够调用其指针方法的。这是因为在go内部做了自动转换。例如,若add方法是指针方法,那么表达式i1.add(2)会被自动转换为(&i1).add(2)


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP