区块链是借由密码学[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)