实践 # 基于Go语言自己动手实现区块链_go 区块链
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
0 介绍
学习目标1、理解什么是区块链2、掌握区块链基本结构3、构建区块链基本模型
- 理论部分1、区块链七层架构模型2、区块链链式结构
- 实践部分1、创建区块2、创建区块的“链”3、访问区块链
开发环境GoLand 付费学生可以申请免费需要提供证明
区块链的理论基础部分可以参考我写的其他博客本文主要介绍实践。
- 没有Go语言基础: 10min快速入门 video
开源代码https://github.com/hexbo/go-blockchain-demo
如果有帮助欢迎star和分享~也欢迎提pr
1 区块链理论基础
数据层
hash
hash函数的特点单向性、确定性、隐蔽性、抗篡改、抗碰撞
区块链最基本的技术是hash这里给出用go写的一个hash的demo:
package main
import (
"crypto/sha256"
"encoding/hex"
"log"
)
func calcHash(toBeHashed string) string {
hashInBytes := sha256.Sum256([]byte(toBeHashed))
hashInStr := hex.EncodeToString(hashInBytes[:])
log.Printf("%s %s", toBeHashed, hashInStr)
return hashInStr
}
func main() {
calcHash("test1")
}
如果不熟悉Go关于上述代码可能会有以下问题
-
Go语言字符串
string
和字节数组[]byte
类型转换问题【Golang】字符咋存utf8咋编码string啥结构、golang中字符串与数组切片的转换 -
Go语言数组、切片的转换问题数组和切片的相互转换
说明sha256返回的是[32]byte,通过hashInBytes[:]将数组类型转为slice即[]byte -
Go语言数组/切片传参问题【Go基础】Go中数组传参的几种方式、Golang 数组切片的值传递与引用传递
2 从0到1实现区块链模型
组成部分1.实现区块和链式结构2.实现一个简单的HTTP Server对外暴露读写接口
说明Go语言函数名大小写小写代表方法只能在当前包使用是私有方法大写代表公有方法。
区块 Block
package core
import (
"crypto/sha256"
"encoding/hex"
"time"
)
type Block struct {
Index int64 // 区块编号
Timestamp int64 // 区块时间戳
PrevBlockHash string // 上一个区块哈希值
Hash string // 当前区块哈希
Data string // 区块数据
}
func calculateHash(b Block) string {
blockData := string(b.Index) + string(b.Timestamp) + b.PrevBlockHash + b.Data
hashInBytes := sha256.Sum256([]byte(blockData))
return hex.EncodeToString(hashInBytes[:])
}
func GenerateNewBlock(preBlock Block, data string) Block {
newBlock := Block{}
newBlock.Index = preBlock.Index + 1
newBlock.PrevBlockHash = preBlock.Hash
newBlock.Timestamp = time.Now().Unix()
newBlock.Data = data
newBlock.Hash = calculateHash(newBlock)
return newBlock
}
func GenerateGenesisBlock() Block {
preBlock := Block{}
preBlock.Index = -1
preBlock.Hash = ""
return GenerateNewBlock(preBlock, "Genesis Block")
}
链式结构
代码说明参数名在函数前是结构函数的语法类似C++的类成员函数GO里都是绑定到结构体上的。
package core
import (
"fmt"
"log"
)
type Blockchain struct {
Blocks []*Block
}
func NewBlockchain() *Blockchain {
genesisBlock := GenerateGenesisBlock()
blockchain := Blockchain{}
blockchain.AppendBlock(&genesisBlock)
return &blockchain
}
func (bc *Blockchain) SendData(data string) {
preBlock := bc.Blocks[len(bc.Blocks)-1]
newBlock := GenerateNewBlock(*preBlock, data)
bc.AppendBlock(&newBlock)
}
func (bc *Blockchain) AppendBlock(newBlock *Block) {
if len(bc.Blocks) == 0 {
bc.Blocks = append(bc.Blocks, newBlock)
return
}
if isValid(*newBlock, *bc.Blocks[len(bc.Blocks)-1]) {
bc.Blocks = append(bc.Blocks, newBlock)
} else {
log.Fatal("invalid block")
}
}
func (bc *Blockchain) Print() {
for _, block := range bc.Blocks {
fmt.Printf("Index: %d\n", block.Index)
fmt.Printf("PrevHash: %s\n", block.PrevBlockHash)
fmt.Printf("CurrHash: %s\n", block.Hash)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Timestamp: %d\n", block.Timestamp)
}
}
func isValid(newBlock Block, oldBlock Block) bool {
if newBlock.Index-1 != oldBlock.Index {
return false
}
if newBlock.PrevBlockHash != oldBlock.Hash {
return false
}
if calculateHash(newBlock) != newBlock.Hash {
return false
}
return true
}
【问题】如下代码: 这里的len函数在这里不会冗余吗
C的strlen需要对char数组遍历一遍因此相当于O(n)的复杂度不是O(1)不清楚Go的len函数
if isValid(*newBlock, *bc.Blocks[len(bc.Blocks)-1]) {
说明参考上面给出的一个关于go string存储的问题go在字符串指针开头存了字符串长度数据而不是在末尾存’\0’所以应该是可以O(1)查询。
CMD验证
package main
import "go-blockchain/core"
func main() {
bc := core.NewBlockchain()
bc.SendData("Send 1 BTC to Alice")
bc.SendData("Send 1 EOS to Bob")
bc.Print()
}
HTTP RPC读写
package main
import (
"encoding/json"
"go-blockchain/core"
"io"
"net/http"
)
var blockchain *core.Blockchain
func run() {
http.HandleFunc("/blockchain/get", blockchainGetHandler)
http.HandleFunc("/blockchain/write", blockchainWriteHandler)
http.ListenAndServe("localhost:8888", nil)
}
func blockchainGetHandler(w http.ResponseWriter, r *http.Request) {
bytes, error := json.Marshal(blockchain)
if error != nil {
http.Error(w, error.Error(), http.StatusInternalServerError)
return
}
io.WriteString(w, string(bytes))
}
func blockchainWriteHandler(w http.ResponseWriter, r *http.Request) {
blockData := r.URL.Query().Get("data")
blockchain.SendData(blockData)
blockchainGetHandler(w, r)
}
func main() {
blockchain = core.NewBlockchain()
run()
}
运行后访问localhost:8888/blockchain/get可以查看当前区块数据访问/write?data="xxxx"可以写数据
Reference
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |