区块链系统教程

go实现pos算法代码

2018-05-26  本文已影响9人  豆瓣奶茶

这是自己显示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)
    }

}
上一篇下一篇

猜你喜欢

热点阅读