“This is the 17th day of my participation in the Gwen Challenge in November. See details of the event: The Last Gwen Challenge in 2021”.

A Blockchain Instance Analysis Based on PoS Consensus Algorithm (Upgraded Version)

This article appears in my column: Blockchain in Detail

This column will describe the blockchain consensus algorithm, ethereum smart contract, super ledger smart contract, EOS smart contract related knowledge, and also introduce several practical projects in detail. If possible, we can also read the source code of Ethereum together. If you are interested, let’s learn block chain technology together

One, foreword

Previously we briefly introduced an example based on PoS consensus algorithm, today we will parse an upgraded version of the example. Like, follow and bookmark bloggers if you like them

2. Some data structures in this example

type Block struct {
    Index     int
    TimeStamp string
    BPM       int
    HashCode  string
    PrevHash  string
    Validator string
}
​
var Blockchain []Block
var tempBlocks []Block
​
var candidateBlocks = make(chan Block)
​
var announcements = make(chan string)
​
var validators = make(map[string]int)
Copy the code

First, a Block structure is defined, and then a Blockchain is defined, which is actually a Block array. This tempBlocks is a block buffer. CandidateBlocks are candidateBlocks, and when any node proposes a new block, it is sent to this pipeline. Announcements are channels for broadcasting. Validators are lists of validators, nodes and tokens they possess.

Generate blocks and compute hashes

func generateBlock(oldBlock Block, BPM int, address string) Block {
    var newBlock Block
    newBlock.Index = oldBlock.Index + 1
    newBlock.TimeStamp = time.Now().String()
    newBlock.BPM = BPM
    newBlock.PrevHash = oldBlock.HashCode
    newBlock.Validator = address
    newBlock.HashCode = GenerateHashValue(newBlock)
    return newBlock
}
​
func GenerateHashValue(block Block) string {
    var hashcode = block.PrevHash +
        block.TimeStamp + block.Validator +
        strconv.Itoa(block.BPM) + strconv.Itoa(block.Index)
    return calculateHash(hashcode)
}
​
func calculateHash(s string) string {
    var sha = sha256.New()
    sha.Write([]byte(s))
    hashed := sha.Sum(nil)
    return hex.EncodeToString(hashed)
}
Copy the code

This is really in every previous example, here really do not want to talk about, do not understand the small partners can take a look at the previous example of this column.

4. Master logic

func main(a) {
    err := godotenv.Load()
    iferr ! =nil {
        log.Fatal(err)
    }
​
    genesisBlock := Block{}
    genesisBlock = Block{0, time.Now().String(), 0,
        GenerateHashValue(genesisBlock), "".""}
    spew.Dump(genesisBlock)
​
    Blockchain = append(Blockchain, genesisBlock)
​
    port := os.Getenv("PORT")
​
    server, err := net.Listen("tcp".":"+port)
    iferr ! =nil {
        log.Fatal(err)
    }
​
    log.Println("HTTP Server Listening on port :", port)
​
    defer server.Close()
​
    go func(a) {
        for cadidate := range candidateBlocks {
​
            mutex.Lock()
​
            tempBlocks = append(tempBlocks, cadidate)
            mutex.Unlock()
        }
    }()
​
    go func(a) {
        for {
​
            pickWinner()
        }
    }()
​
    for {
        conn, err := server.Accept()
        iferr ! =nil {
            log.Fatal(err)
        }
        go handleConn(conn)
    }
}
Copy the code

Let’s take a look at the main logic. We’ll start by loading our local. Env file, which can store a lot of parameters, in this case we’ll store port 9000.

And then we create the creation block, and the creation block is zero in height.

spew.Dump(genesisBlock)
Copy the code

Is to format the genesis block output through the command line.

Blockchain = append(Blockchain, genesisBlock)
Copy the code

This line of code adds genesis block to the blockchain.

port := os.Getenv("PORT")
Copy the code

Env has a port number stored in the file. Here we get the port number from this file into the port variable.

The server process is then started to listen for the port acquired above.

defer server.Close()
Copy the code

Make it a habit to start a service and write a delayed shutdown, otherwise you will forget to release resources later.

This is followed by a concurrent operation that loops through candidateBlocks and reads it into the buffer as soon as a block enters the pipeline. It then decides which node should be mined.

Then continuously receive the connection of the verifier node, connected to process the information sent by the terminal.

5. Nodes that obtain accounting rights

func pickWinner(a) {
    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 <- "\nvalidator:" + lotteryWinner + "\n"
                }
                break
            }
        }
​
    }
    mutex.Lock()
    tempBlocks = []Block{}
    mutex.Unlock()
}
Copy the code

Here is the essence of PoS. According to the number of tokens, the nodes with accounting rights are determined.

First, you have to take a 30-second break every time you select a node that has billing rights.

Each time a node with billing rights is selected, a partial copy of the block of the buffer is made, and the copy is manipulated.

We start by declaring a lottery pool to place the verifier address.

It then determines whether the buffer is empty, iterates over the buffer copy if it is not, continues if the block’s verifier is in the lottery pool, and does what follows if it is not.

Then get a copy of the verifier list, get the number of token tokens above the verifier node that is not in the lottery pool, and add as many verifier address strings to the lottery pool as the number of tokens.

Once the pool is filled, the lottery begins to pick the lucky winners. Select by random number, and then add the winner’s block to the block chain, and broadcast the winner’s block message.

If the temporary buffer is empty, we will make it equal to an empty block.

Handle command line requests


func handleConn(conn net.Conn) {

    defer conn.Close()

    go func(a) {

            msg := <-announcements
            io.WriteString(conn, msg)
        }
    }()

    var address string

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

    scanBalance := bufio.NewScanner(conn)
    for scanBalance.Scan() {

        balance, err := strconv.Atoi(scanBalance.Text())
        iferr ! =nil {
            log.Printf("%v not a number: %v", scanBalance.Text(), err)
            return
        }

        address = calculateHash(time.Now().String())
   
        validators[address] = balance
        fmt.Println(validators)
        break
    }

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

    scanBPM := bufio.NewScanner(conn)
    go func(a) {

        for {
            for scanBPM.Scan() {
                bmp, err := strconv.Atoi(scanBPM.Text())
                iferr ! =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 := generateBlock(oldLastIndex, bmp, address)
                iferr ! =nil {
                    log.Println(err)
                    continue
                }
     
                if isBlockValid(newBlock, oldLastIndex) {
         
                    candidateBlocks <- newBlock
                }
            }
        }
    }()
​
    for {
        time.Sleep(time.Second * 20)
        mutex.Lock()
        output, err := json.Marshal(Blockchain)
        mutex.Unlock()
        iferr ! =nil {
            log.Fatal(err)
        }
 
        io.WriteString(conn, string(output)+"\n")}}func isBlockValid(newBlock, oldBlock Block) bool {
    if oldBlock.Index+1! = newBlock.Index {return false
    }
    ifoldBlock.HashCode ! = newBlock.PrevHash {return false
    }
    ifGenerateHashValue(newBlock) ! = newBlock.HashCode {return false
    }
    return true
}
Copy the code

It starts with a delayed release of connected resources.

defer conn.Close()
Copy the code

The lucky winner message is then read from the pipe and output to the conn connection.

Then receive the number of tokens for that node in the command line window.

The verifier’s address is then generated based on the current time.

address = calculateHash(time.Now().String())
Copy the code

Save the validators’ addresses and tokens into validators.

Then enter the trade information as prompted. If the entered transaction information is illegal, the node is deleted.

delete(validators, address)
conn.Close()
Copy the code

The logic is to take a block, generate new block information, and simply verify that the block is valid. If so, put the block into the candidateBlocks pipeline and wait for the lucky draw.

The way to verify that the block is valid here is simply to verify that the height of the current block is not the previous block plus one, and then determine whether the PrevHash of the new block is equal to the hash of the previous block. Then check again to see if the hash is correct.

7. Operation results