相对于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)
}
}
以上就是全部的实现过程,仅供参考
网友评论