A blockchain instance analysis based on PoS consensus algorithm (upgraded version)

Author: Regan Yue

Source: Hang Seng LIGHT cloud community

1, Foreword

Earlier, we briefly introduced an example based on PoS consensus algorithm. Today, we will analyze an upgraded example. If you like bloggers, remember to praise, pay attention and collect~

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)

First, define a Block structure Block, and then define a Blockchain blockchain, which is actually a Block array. This tempBlocks is a Block buffer. Candidate blocks are candidate blocks. When any node proposes a new Block, it will send it to the pipeline. announcements are channels to broadcast. Validators are a list of validators, including node addresses and tokens owned by them.

3, Generating blocks and calculating 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)
}

I've talked about every previous example. I really don't want to talk about it here. If you don't understand, you can take a look at the examples in front of this column.

4, Main logic

func main() {
    err := godotenv.Load()
    if err != 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)
    if err != nil {
        log.Fatal(err)
    }

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

    defer server.Close()

    go func() {
        for cadidate := range candidateBlocks {

            mutex.Lock()

            tempBlocks = append(tempBlocks, cadidate)
            mutex.Unlock()
        }
    }()

    go func() {
        for {

            pickWinner()
        }
    }()

    for {
        conn, err := server.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleConn(conn)
    }
}

Let's take a look at the main logic. First, load the local logic env file, which can store many parameters. Here we store a port number 9000

Then create the genesis block. Note that the height of the genesis block is 0

spew.Dump(genesisBlock)

Is to format and output the creation block through the command line.

Blockchain = append(Blockchain, genesisBlock)

This line of code adds the genesis block to the blockchain.

port := os.Getenv("PORT")

I said earlier env file stores the port number. Here, get the port number in this file into the port variable.

Then start the service process to listen to the port obtained above.

defer server.Close()

We should form the habit of writing delayed shutdown when starting the service, otherwise we will forget to release resources later.

Then there is the concurrent operation. The candidate blocks are read in a loop. Once a block enters the pipeline, it is immediately read to the buffer. Then determine which node should mine concurrently.

Then continuously receive the connection of the verifier node, connect it and process the information sent by the terminal.

5, Node for obtaining bookkeeping right

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 <- "\nvalidator:" + lotteryWinner + "\n"
                }
                break
            }
        }

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

Here is the essence of PoS, which determines the nodes with bookkeeping rights according to the number of token tokens.

First, every time you select a node with bookkeeping rights, you have to rest for 30 seconds. You can't keep selecting it.

Before selecting the node with accounting right each time, copy a part of the buffer block, and then operate the copy.

We first specify a lottery pool to place the verifier's address.

Then judge whether the buffer is empty. If the buffer copy is not empty, traverse the buffer copy. Then, if the verifier of the block is in the lottery pool, continue to traverse. If not, execute the following contents.

Then, obtain a copy of the verifier list, obtain the number of token tokens of the verifier node not in the lottery pool, and then add as many verifier address strings as the number of tokens to the lottery pool and put them into the lottery pool.

After the lottery pool is filled, we begin to choose the lucky ones. Select by random number, then add the winner's block to the blockchain, and then broadcast the winner's block message.

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

6, Processing command line requests

func handleConn(conn net.Conn) {

    defer conn.Close()

    go func() {
        for {
            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())
        if err != 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() {

        for {
            for scanBPM.Scan() {
                bmp, 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 := generateBlock(oldLastIndex, bmp, address)
                if err != 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()
        if err != nil {
            log.Fatal(err)
        }

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

}

func isBlockValid(newBlock, oldBlock Block) bool {

    if oldBlock.Index+1 != newBlock.Index {
        return false
    }

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

    if GenerateHashValue(newBlock) != newBlock.HashCode {
        return false
    }
    return true
}

First, delay the release of connection resources.

defer conn.Close()

Then read the message of the lucky one from the pipeline and output it to the connection conn.

Then, the number of tokens of the node is received in the command line window.

Then generate the verifier's address according to the current time.

address = calculateHash(time.Now().String())

Then save the verifier's address and the tokens he owns into validators.

Then enter the transaction information according to the prompt. If the transaction information entered is illegal, the node will be deleted.

delete(validators, address)
conn.Close()

The subsequent logic is to take a block, then generate new block information, and then simply verify whether the block is legal. If it is legal, put the block into the candidate blocks pipeline and wait for the lucky ones to be extracted.

Here, the method to verify whether the block is legal is very simple, that is, to verify whether the height of the current block is one plus the previous module, and then judge whether the PrevHash of the new block is equal to the hash value of the previous block. Then verify that the hash value is correct again.

7, Operation results

Want to learn more from technology bosses? Where to discuss the problems encountered in development? How to obtain massive financial technology resources?

Hang Seng LIGHT cloud community , the financial technology professional community platform built by Hang Seng electronics shares practical technology dry goods, resource data and financial technology industry trends, and embraces all financial developers.

Scan the QR code of the applet below and join us!

Keywords: Blockchain

Added by Templar on Thu, 13 Jan 2022 04:28:18 +0200