參考連接1html
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%的工做量證實的哈希計算能力。算法
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 }