【go共識算法】-POS

pos介紹

參考連接1html

pos概念

Proof of Stake,股權證實。
PoS核心概念爲幣齡,即持有貨幣的時間。例若有10個幣、持有90天,即擁有900幣天的幣齡。
另外使用幣,即意味着幣齡的銷燬。
在PoS中有一種特殊的交易稱爲利息幣,即持有人能夠消耗幣齡得到利息,同時得到爲網絡產生區塊、以及PoS造幣的優先權。node

點點幣應用

第一個基於PoS的虛擬幣是點點幣。鑑於PoW的缺陷,2012年Sunny King提出了PoS,並基於PoW和PoS的混合機制發佈了點點幣PPCoin。前期採用PoW挖礦開採和分配貨幣,以保證公平。後期採用PoS機制,保障網絡安全,即擁有51%貨幣難度更大,從而防止51%攻擊。git

點點幣(Peercoin)是首先採用權益證實的貨幣,點點幣在SHA256的哈希運算的難度方面引入了幣齡的概念,使得難度與交易輸入的幣齡成反比。在點點幣中,幣齡被定義爲幣的數量與幣所擁有的天數的乘積,這使得幣齡可以反映交易時刻用戶所擁有的貨幣數量。實際上,點點幣的權益證實機制結合了隨機化與幣齡的概念,未使用至少30天的幣能夠參與競爭下一區塊,越久和越大的幣集有更大的可能去簽名下一區塊。github

然而,一旦幣的權益被用於簽名一個區塊,則幣齡將清爲零,這樣必須等待至少30日才能簽署另外一區塊。同時,爲防止很是老或很是大的權益控制區塊鏈,尋找下一區塊的最大機率在90天后達到最大值,這一過程保護了網絡,並隨着時間逐漸生成新的幣而無需消耗大量的計算能力。點點幣的開發者聲稱這將使得惡意攻擊變得困難,由於沒有中心化的挖礦池需求,並且購買半數以上的幣的開銷彷佛超過得到51%的工做量證實的哈希計算能力。算法

優缺點

  • 優勢: 縮短了共識達成的時間,鏈中共識塊的速度更快,再也不須要大量消耗能源挖礦。做弊得不嘗失,由於若是一名持有 51% 以上股權的人做弊,至關於他坑了本身,由於他是擁有股權最多的人,做弊致使的結果每每是擁有着越多的損失越多。
  • 缺點: 攻擊成本低,只有節點有物品數量,例如代幣數量,就能發起髒數據的區塊攻擊,另外擁有代幣數量大的節點得到記帳權的機率會更大,會使得網絡共識受少數富裕帳戶支配,從而失去公正性。

go實現pos算法

csdn
騰訊雲
簡書
.envjson

PORT=9000

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"
)

// Block represents each 'item' in the blockchain
type Block struct {
    Index     int
    Timestamp string
    BPM       int
    Hash      string
    PrevHash  string
    Validator string
}

var Blockchain []Block // Blockchain is a series of validated Blocks
var tempBlocks []Block // tempBlocks是臨時存儲單元,在區塊被選出來並添加到BlockChain以前,臨時存儲在這裏

// candidateBlocks handles incoming blocks for validation
var candidateBlocks = make(chan Block)

// announcements broadcasts winning validator to all nodes
var announcements = make(chan string)

var mutex = &sync.Mutex{}

// validators keeps track of open validators and balances
var validators = make(map[string]int)

func main() {

    err := godotenv.Load("./test/example.env")
    if err != nil {
        log.Fatal(err)
    }

    // create genesis block
    t := time.Now()
    genesisBlock := Block{}
    genesisBlock = Block{0, t.String(), 0, calculateBlockHash(genesisBlock), "", ""}
    spew.Dump(genesisBlock)
    Blockchain = append(Blockchain, genesisBlock)

    httpPort := os.Getenv("PORT")

    // start TCP and serve TCP server
    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)
    }
}

// pickWinner creates a lottery pool of validators and chooses the validator who gets to forge a block to the blockchain
// by random selecting from the pool, weighted by amount of tokens staked
func pickWinner() {
    time.Sleep(30 * time.Second)
    mutex.Lock()
    temp := tempBlocks
    mutex.Unlock()

    lotteryPool := []string{}
    if len(temp) > 0 {

        // slightly modified traditional proof of stake algorithm
        // from all validators who submitted a block, weight them by the number of staked tokens
        // in traditional proof of stake, validators can participate without submitting a block to be forged
    OUTER:
        for _, block := range temp {
            // if already in lottery pool, skip
            for _, node := range lotteryPool {
                if block.Validator == node {
                    continue OUTER
                }
            }

            // lock list of validators to prevent data race
            mutex.Lock()
            setValidators := validators
            mutex.Unlock()

            k, ok := setValidators[block.Validator]
            if ok {
                for i := 0; i < k; i++ {
                    lotteryPool = append(lotteryPool, block.Validator)
                }
            }
        }

        // randomly pick winner from lottery pool
        s := rand.NewSource(time.Now().Unix())
        r := rand.New(s)
        lotteryWinner := lotteryPool[r.Intn(len(lotteryPool))]

        // add block of winner to blockchain and let all the other nodes know
        for _, block := range temp {
            if block.Validator == lotteryWinner {
                mutex.Lock()
                Blockchain = append(Blockchain, block)
                mutex.Unlock()
                for _ = range validators {
                    announcements <- "\nwinning validator: " + lotteryWinner + "\n"
                }
                break
            }
        }
    }

    mutex.Lock()
    tempBlocks = []Block{}
    mutex.Unlock()
}

func handleConn(conn net.Conn) {
    defer conn.Close()

    go func() {
        for {
            msg := <-announcements
            io.WriteString(conn, msg)
        }
    }()
    // validator address
    var address string

    // tokens數量由用戶從控制檯輸入
    io.WriteString(conn, "Enter token balance:")
    scanBalance := bufio.NewScanner(conn)
    // 獲取用戶輸入的balance值,並打印出來
    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 // 只循環一次
    }

    // 循環輸入BPM
    io.WriteString(conn, "\nEnter a new BPM:")

    scanBPM := bufio.NewScanner(conn)

    go func() {
        for {
            // take in BPM from stdin and add it to blockchain after conducting necessary validation
            for scanBPM.Scan() {
                bpm, err := strconv.Atoi(scanBPM.Text())
                // if malicious party tries to mutate the chain with a bad input, delete them as a validator and they lose their staked tokens
                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) {
                    // main func 中 for candidate := range candidateBlocks ,將newBlock 追加到 tempBlocks中
                    candidateBlocks <- newBlock
                }
                io.WriteString(conn, "\nEnter a new BPM:")
            }
        }
    }()

    // simulate receiving broadcast
    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")
    }

}

// isBlockValid makes sure block is valid by checking index
// and comparing the hash of the previous block
func isBlockValid(newBlock, oldBlock Block) bool {
    if oldBlock.Index+1 != newBlock.Index {
        return false
    }

    if oldBlock.Hash != newBlock.PrevHash {
        return false
    }

    if calculateBlockHash(newBlock) != newBlock.Hash {
        return false
    }

    return true
}

// SHA256 hasing
// calculateHash is a simple SHA256 hashing function
func calculateHash(s string) string {
    h := sha256.New()
    h.Write([]byte(s))
    hashed := h.Sum(nil)
    return hex.EncodeToString(hashed)
}

//calculateBlockHash returns the hash of all block information
func calculateBlockHash(block Block) string {
    record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash
    return calculateHash(record)
}

// generateBlock creates a new block using previous block's hash
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
}
相關文章
相關標籤/搜索