这是自己显示pos共识算法的代码,
package main
import (
"github.com/joho/godotenv"
"log"
"time"
"fmt"
"crypto/sha256"
"encoding/hex"
"github.com/davecgh/go-spew/spew"
"net"
"os"
"sync"
"io"
"bufio"
"strconv"
"math/rand"
"encoding/json"
)
//区块的信息
type Block struct {
Index int
Timestamp string
BMP int
Hash string
PrevHash string
Validator string
}
//Blockchain是我们的官方区块链,它只是一串经过验证的区块集合。
// 每个区块中的PrevHash与前面块的Hash相比较,以确保我们的链是正确的
var Blockchain []Block
//tempBlocks是临时存储单元,在区块被选出来并添加到BlockChain之前,一系列的区块都会存储在这个
var tempBlocks []Block
//candidateBlocks是Block的通道,任何一个节点在提出一个新块时都将它发送到这个通道
var chan_candidateBlocks = make(chan Block)
//announcements也是一个通道,我们的主Go TCP服务器将向所有节点广播最新的区块链
var chan_announcements = make(chan string)
//validators是存储节点的map,同时也会保存每个节点持有的令牌数
var validators = make(map[string]int)
var mutex = sync.Mutex{}
//根据区块计算hash
func calculateBlockHash(block Block)string{
blockStr := string(block.Index) + block.Timestamp + string(block.BMP) + block.PrevHash;
return calculateHash(blockStr)
}
func calculateHash(s string)string{
var h = sha256.New()
h.Write([]byte(s))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
//生成区块的函数
func generateBlock(oldBlock Block,BMP int,address string)(Block,error){
var newBlock Block
t := time.Now()
newBlock.Index = oldBlock.Index +1
newBlock.Timestamp = t.String()
newBlock.BMP = BMP
newBlock.PrevHash = oldBlock.Hash
newBlock.Hash = calculateBlockHash(newBlock)
newBlock.Validator = address
return newBlock,nil
}
func isBlockValid(newBlock,oldBlock Block)bool{
if newBlock.PrevHash != oldBlock.Hash {
return false
}
if newBlock.Index != oldBlock.Index+1 {
return false
}
if calculateBlockHash(newBlock) != newBlock.Hash {
return false
}
return true
}
func handleConn(conn net.Conn){
fmt.Println("连接成功",conn.LocalAddr())
defer conn.Close()
go func(){
for {
//把通道中的数据不断的发给验证者客户端,通道是存在在服务器上的
msg := <- chan_announcements
io.WriteString(conn,msg)
}
}()
//让用户输入balace,生成address
var address string
io.WriteString(conn,"Enter token balance :")
scanBalance := bufio.NewScanner(conn)
for scanBalance.Scan(){
balance,err := strconv.Atoi(scanBalance.Text())
if err != nil {
log.Fatal(err)
return
}
t := time.Now()
address = calculateHash(t.String())
validators[address] = balance
fmt.Println(validators)
break
}
//在控制台让用户输入BMP
io.WriteString(conn,"\nEnter a new BPM :")
scanBMP := bufio.NewScanner(conn)
go func() {
for {
for scanBMP.Scan(){
bmp,err := strconv.Atoi(scanBMP.Text())
fmt.Println("又接收到了BMP")
if err != nil {
log.Printf("%v not a number:%v",scanBMP.Text(),err)
//用户输入的bmp不是int,就相当于作恶,直接删除
delete(validators,address)
}
mutex.Lock()
oldLastIndex := Blockchain[len(Blockchain)-1]
mutex.Unlock()
newBlock,err := generateBlock(oldLastIndex,bmp,address)
if err != nil {
log.Println(err)
continue
}
if isBlockValid(newBlock,oldLastIndex) {
chan_candidateBlocks <- newBlock
}
io.WriteString(conn, "\nEnter a new BPM:")
}
}
}()
//模拟发送广播,就是服务器把最新的区块信息每隔1分钟就发送给客户端
for {
time.Sleep(time.Minute)
mutex.Lock()
output, err := json.Marshal(Blockchain)
outputTempBlock,_ := json.Marshal(tempBlocks)
outputvalidators,_:=json.Marshal(validators)
mutex.Unlock()
if err != nil {
log.Fatal(err)
}
io.WriteString(conn,"------------\n")
io.WriteString(conn,"Blockchain\n")
io.WriteString(conn, string(output)+"\n")
io.WriteString(conn,"tempBlock\n")
io.WriteString(conn,string(outputTempBlock)+"\n")
io.WriteString(conn,"validators\n")
io.WriteString(conn,string(outputvalidators)+"\n")
io.WriteString(conn,"------------\n")
}
}
//这里是PoS的主题逻辑。我们需要编写代码以实现获胜验证者的选择;他们所持有的令牌数量越高,他们就越有可能被选为胜利者。
//为了简化代码,我们只会让提出新块儿的验证者参与竞争。在传统的PoS,一个验证者即使没有提出一个新的区块,也可以被选为胜利者。
// 切记,PoS不是一种确定的定义(算法),而是一种概念,因此对于不同的平台来说,可以有不同的PoS实现。
func pickerWinner(){
time.Sleep(time.Second*20)
fmt.Println("开始选举")
mutex.Lock()
temp := tempBlocks
mutex.Unlock()
//用来存放验证者address的数组,验证者的币越多,在数据的元素就越多
lotteryPool := []string{}
if len(temp)>0 {
OUTER:
//把所有候选区块的验证者按照币的权重放入到奖池中(lotteryPool)
for _,block := range temp{
//判断此区块的验证者是否已经存在奖池中,如果存在就什么都不干了.
//这里就解决了每一论中,即使你多次提交了区块,服务器也只用一个你的区块
for _,node := range lotteryPool {
if block.Validator == node {
continue OUTER
}
}
fmt.Println("here1")
mutex.Lock()
setValidators := validators
mutex.Unlock()
//得到validator地址锁定的币
k,ok := setValidators[block.Validator]
if ok {
for i := 0; i < k; i++ {
lotteryPool = append(lotteryPool,block.Validator)
}
}
}
//从奖池中随机挑选一个候选者
fmt.Println("随机选择获胜者了")
s := rand.NewSource(time.Now().Unix())
r := rand.New(s)
lotteryWinner := lotteryPool[r.Intn(len(lotteryPool))]
for _, block := range temp {
if block.Validator == lotteryWinner {
mutex.Lock()
Blockchain = append(Blockchain, block)
mutex.Unlock()
//选举成功后,给所有节点都发送消息
for _ = range validators {
chan_announcements <- "\nwinning validator: " + lotteryWinner + "\n"
}
break
}
}
mutex.Lock()
tempBlocks = []Block{}
mutex.Unlock()
}
fmt.Println("没有候选区块,就算了吧")
}
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal(err)
}
//创建创世区块
var t =time.Now()
fmt.Println(t)
var genesisBlock Block = Block{}
//下面的语句计算出来的Hash,其实就是个空结构体的hash
genesisBlock = Block{0,t.String(),0,calculateBlockHash(genesisBlock),"",""}
//创建区块链了
Blockchain = append(Blockchain, genesisBlock)
spew.Dump(genesisBlock)
//开始tcp服务器
server,err := net.Listen("tcp",":"+os.Getenv("PORT"))
if err != nil {
log.Fatal(err)
}
defer server.Close()
// 启动了一个Go routine 从 candidateBlocks 通道中获取提议的区块,然后填充到临时缓冲区 tempBlocks 中
go func() {
for candidateBlock := range chan_candidateBlocks{
mutex.Lock()
tempBlocks= append(tempBlocks,candidateBlock)
mutex.Unlock()
}
}()
//选择winner
go func() {
for{
pickerWinner()
}
}()
//处理请求,这个就不要再开一个线程了,不然mian routine直接就结束了,大家就都结束了...
for {
conn,err := server.Accept()
if err!= nil {
log.Fatal(err)
}
go handleConn(conn)
}
}
网友评论