共識演算法PoS及Go語言實現

weixin_33924312發表於2018-10-09

相對於pow來說,pos共識演算法在計算方面有了很大的進步,因為不需要去進行像pow一樣的“難度解密”,取代他的而是對於股權的證明。在pos當中,塊是已經生成的,我們生成新的塊的過程是基於每個節點的“財產”的數量,如果一個驗證者他身上的資產越多,那麼他獲取下一個區塊的記賬權的可能性會越大。

實現Proof of Stake主要功能點

·我們將有一箇中心化的TCP伺服器節點,其他的節點可以連線到這個伺服器上

·最新的區塊鏈的狀態將定期的廣播到每個節點上

·每個節點都可以公平的提議建立新的區塊

·基於每個節點的“資產”的數量,選舉出一個獲勝者,並將獲勝者(區塊)新增到區塊鏈當中去

實現PoS

設定TCP伺服器的埠

新建 .env 檔案,新增 PORT=9000

安裝依賴軟體

$ go get github.com/davecgh/go-spew/spew

$ go get github.com/joho/godotenv

和之前的Pow演算法一樣,需要使用spew和godotenv來輔助實現

引入相應的包

新建main.go檔案


package main

import(

        "bufio"

        "crypto/sha256"

        "encoding/hex"

        "encoding/json"

        "fmt"

        "io"

        "log"

        "math/rand"

        "net"

        "os"

        "strconv"

        "sync"

        "time"

        "github.com/davecgh/go-spew/spew"

        "github.com/joho/godotenv"

)

定義全域性變數


type Blockstruct{

        Index        int

        Timestamp    string

        BPM          int

        PrevHash    string

        Hash        string

        Validator    string//由原先的difficulty改為了現在validator

}

var Blockchain []Block

var tempBlocks []Block// tempBlocks 是臨時儲存單元,在區塊被選出來並新增到 BlockChain 之前,臨時儲存在這裡

var candidateBlocks = make(chan Block)//chan建立的是一個fifo佇列,fifo先進先出

//candidateBlocks 是 Block 的通道,任何一個節點在提出一個新塊時都將它傳送到這個通道

var announcements = make(chan string)// announcements 也是一個通道,我們的主Go TCP伺服器將向所有節點廣播最新的區塊鏈

var mutex = &sync.Mutex{}

var validators = make(map[string]int)//validators 是節點的儲存map,同時也會儲存每個節點持有的令牌數

// map初始化使用make函式

生成區塊函式


func generateBlock(oldBlock Block, BPM int, address string) (Block, error) {

        var newBlock Block

        t := time.Now()

        newBlock.Index = oldBlock.Index +1

        newBlock.Timestamp = t.String()

        newBlock.BPM = BPM

        newBlock.PrevHash = oldBlock.Hash

        newBlock.Hash = calculateBlockHash(newBlock)

        newBlock.Validator = address

        return newBlock, nil

}

func calculateHash(s string) string {

        h := sha256.New()

        h.Write([]byte(s))

        hashed := h.Sum(nil)

        return hex.EncodeToString(hashed)

}

//calculateBlockHash 是對一個 block 進行 hash,將一個 block 的所有欄位連線到一起後,再呼叫 calculateHash 將字串轉為 SHA256 hash 。

func calculateBlockHash(block Block) string {

        record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash

        return calculateHash(record)

}

驗證區塊內容


func isBlockValid(newBlock, oldBlock Block) bool {

        if newBlock.Index != oldBlock.Index +1{

                return false

        }

        if newBlock.PrevHash != oldBlock.Hash{

                return false

        }

        if calculateBlockHash(newBlock) != oldBlock.Hash{

                return false

        }

        return true

}

驗證者


func handleConn(conn net.Conn) {

        defer conn.Close()//defer語句延遲執行一個函式,該函式被推遲到當包含它的程式返回時(包含它的函式 執行了return語句/執行到函式結尾自動返回/對應的goroutine panic)執行。

        go func(){

                for {

                        msg := <-announcements

                        io.WriteString(conn, msg)

                }

}()

// 驗證者地址

         var address string

// 驗證者輸入他所擁有的 tokens,tokens 的值越大,越容易獲得新區塊的記賬權

        io.WriteString(conn,"Enter token balance:")

// 允許驗證者輸入他持有的令牌數量,然後,該驗證者被分配一個 SHA256地址,隨後該驗證者地址和驗證者的令牌數被新增到驗證者列表validators 中。

        scanBalance := bufio.NewScanner(conn)

        for scanBalance.Scan(){

                balance, err := strconv.Atoi(scanBalance.Text())

                if err != nil {

                        log.Printf("%v not a number: %v", scanBalance.Text(), err)

                        return

                }

                t := time.Now()

                address = calculateHash(t.String())

                validators[address] = balance

                fmt.Println(validators)

                break

         }

        io.WriteString(conn,"\nEnter a new BPM:")

        scanBPM := bufio.NewScanner(conn)

        go func(){

                for {

                        for scanBPM.Scan() {

                                bpm, err := strconv.Atoi(scanBPM.Text())

                                if err != nil {

                                        log.Printf("%v not a number: %v", scanBPM.Text(), err)

                                        delete(validators, address)

                                        conn.Close()

                                }

                                mutex.Lock()

                                oldLastIndex := Blockchain[len(Blockchain) -1]

                                mutex.Unlock()

                                newBlock, err := generateBlock(oldLastIndex, bpm, address)

                                if err != nil {

                                        log.Println(err)

                                        continue

                                }

                                if isBlockValid(newBlock, oldLastIndex) {

                                        candidateBlocks <- newBlock

                                }

                                io.WriteString(conn,"\n Enter a new BPM:")

                         }

                  }

        }()

        for {

                time.Sleep(time.Minute)

                mutex.Lock()

                output, err := json.Marshal(Blockchain)

                mutex.Unlock()

                if err != nil {

                        log.Fatal(err)

                }

                io.WriteString(conn, string(output) +"\n")

        }

}

選擇獲取記賬權的節點


func pickWinner(){

        time.Sleep(30 * time.Second)

        mutex.Lock()

        temp := tempBlocks

        mutex.Unlock()

        lotteryPool := []string{}

        if len(temp) >0{

        OUTER:

                for _, block :=range temp {

                        for _, node :=range lotteryPool {

                                if block.Validator == node {

                                        continue OUTER

                                }

                        }

                        mutex.Lock()

                        setValidators := validators

                        mutex.Unlock()

                        k, ok := setValidators[block.Validator]

                        if ok {

                                for i :=0; i < k; i++ {

                                        lotteryPool = append(lotteryPool, block.Validator)

                                }

                        }

                }

                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 {

                                        announcements <-"\n winning validator: " + lotteryWinner +"\n"

                                }

                                break

                        }

                }

        }

        mutex.Lock()

        tempBlocks = []Block{}

        mutex.Unlock()

}

主函式


func main() {

        err := godotenv.Load()

        if err != nil {

                log.Fatal(err)

        }

// 建立初始區塊

         t := time.Now()

        genesisBlock := Block{}

        genesisBlock = Block{0, t.String(),0, calculateBlockHash(genesisBlock),""," "}

        spew.Dump(genesisBlock)

        Blockchain = append(Blockchain, genesisBlock)

        httpPort := os.Getenv("PORT")

        server, err := net.Listen("tcp",":" + httpPort)

        if err != nil {

                log.Fatal(err)

        }

        log.Println("HTTP Server Listening on port :", httpPort)

        defer server.Close()

        go func() {

                for candidate :=range candidateBlocks {

                        mutex.Lock()

                        tempBlocks = append(tempBlocks, candidate)

                        mutex.Unlock()

                }

        }()

        go func() {

                for {

                        pickWinner()

                }

        }()

        for{

                conn, err := server.Accept()

                if err != nil {

                        log.Fatal(err)

                }

                go handleConn(conn)

        }

}

以上就是全部的實現過程,僅供參考

相關文章