Implementation of a Block Chain in Go Language

Time:2019-8-8

This paper will gradually dismantle several steps to realize the function of block chain.

The basic knowledge you need to master:

  • What is Block Chain
  • Sha256 hash encryption algorithm
  • Go language foundation, including understanding of go routine and channel

Dead work

  • Go get github.com/davecgh/go-spew/spew spew is a very good print output tool, which can output struct and slice data at the terminal.
  • Go get github.com/gorilla/mux MUX can be used to process HTTP requests to help us quickly build a go server
  • Go get github.com/joho/godotenv This package reads the variables in the. env file

The. env file needs to be in the root directory of the project, usually in the main. go location.

  • A IDE for power, such as Goland

Several concepts

  • Mining, mining is actually to solve a class of mathematical problems, get the right to create a block on the existing block chain, and get some rewards, such as bitcoin, ether coin and so on.
  • PoW (Proof of work), in short, PoW is: there is a Nonce value (random value), which is combined with block data to get a hash value through SHA256. If the first N (difficulty) character of the hash value is 0, then even if the mathematical problem is solved, a block can be created.
  • Block chains, block chains are tightly connected. Each block records the hash of a block. If the PrevHash recorded by the currently generated block is different from the previous block, then the generation is invalid. Similarly, if anyone wants to modify data on a block in the block chain, then This will invalidate the whole chain.

Create a project

  • Create project blockChain under SRC of $GOPATH
  • Create files. Env and main. go under blockChain

Write PORT = 8088 in. env file

Packages to be introduced

import (
   "crypto/sha256"
   "encoding/hex"
   "time"
   "os"
   "log"
   "net/http"
   "github.com/gorilla/mux"
   "encoding/json"
   "io"
   "github.com/davecgh/go-spew/spew"
   "sync"
   "strconv"
   "strings"
   "fmt"
   "github.com/joho/godotenv"
   "net"
   "bufio"
)

Block logic

Define Block Block Block Structures

type Block struct {
   Index int// indicates the location of the block chain in which the block is located
   Timestamp string // timestamp for generating blocks
   Data int // Write block data
   Hash string // hash of whole block data SHA256
   PrevHash string // Hash value of the previous block
   Difficulty int//Definition difficulty
   Nonce string // Define a Nonce
}

Define constants and some variables

Const difficulty = 1 // Definition difficulty, which is the prefix of how many zeros a hash contains
Var mutex = & sync.Mutex {}/// Prevents errors caused by concurrent write requests and adds mutex
Var BlockChain [] Block // Define a block chain with all data elements blocked
Var bcServer Chan [] Block // Defines a channel to handle synchronization between nodes

Computational block hashing

/**
Calculating block hash values
 */
func calculateHash(block Block) string {
   Record: = strconv. Itoa (block. Index) + block. Timestamp + strconv. Itoa (block. Data) + block. PrevHash + block. Nonce /// / Gets the string stitching of the current block block and records it according to the index, timestamp, data contained, and hash of the previous block. Nonce value is added together.
   H: = sha256. New ()// Get sha256 hash algorithm
   H.Write ([] byte (record)// Gets the corresponding hash
   hashed := h.Sum(nil)
   Return hex. EncodeToString (hashed) // Convert to String Return
}

Generate new blocks

/**
Generate a block based on the previous block
 */
func generateBlock(oldBlock Block, Data int) (Block, error) {
   var newBlock Block
   t := time.Now()
   New Block. Index = oldBlock. Index + 1 // Index Self-increasing
   NewBlock. Timestamp = t. String ()// timestamp
   New Block. Data = Data // Data
   New Block. PrevHash = oldBlock. Hash /// Hash of the previous block
   New Block. Difficulty = difficulty // difficulty
   // New Block. Hash = calculateHash (new Block) // Calculate the hash of this block
   for i := 0; ; i++ {
      Hex: = fmt. Sprintf ("% x", i) // hexadecimal display
      newBlock.Nonce = hex
      NewHash: = calculateHash (newBlock) // Computational Hash
      if !isHashValid(newHash, newBlock.Difficulty) {
         Fmt. Println (new Hash), "Keep trying! (")"
         Time. Sleep (time. Second) // Executed every 1s
         continue
      } else {
         Fmt. Println (new Hash): "Successful! ""
         newBlock.Hash = newHash
         break
      }
   }
   return newBlock, nil
}

Verify whether the block is legal

/**
Verify whether the block is legal
 */
func isBlockValid(newBlock, oldBlock Block) bool {
   If oldBlock. Index + 1!= newBlock. Index {/// If the index does not inherit from the previous one, the validation does not pass.
      return false
   }
   If oldBlock. Hash!= newBlock. PrevHash {// If hash does not inherit the previous block, validation does not pass
      return false
   }
   If calculateHash (new Block)!= new Block. Hash {/// If the calculated hash is inconsistent, the verification does not pass.
      return false
   }
   return true
}

Verify that hash conforms to PoW

/**
Verify that the prefix of the hash contains difficulty 0
 */
func isHashValid(hash string, difficulty int) bool {
   prefix := strings.Repeat("0", difficulty)
   return strings.HasPrefix(hash, prefix)
}

Choose Long Chain

Because in actual scenarios, block chains may bifurcate, resulting in different lengths of A and B, long chains are chosen as new chains.

/**
Choose the long chain as the correct chain
 */
 func replaceChain(newBlocks []Block) {
   If len (new Blocks) > len (BlockChain) {// Calculate array length
      BlockChain = newBlocks
   }
 }

Synchronization node logic

Implementation of a Block Chain in Go Language
As shown in the figure, node data synchronization is accomplished by generating a block in a new node, passing it to the main thread through channel, and then broadcasting the main thread to each node.

Listening connection logic

/**
Processing connections
 */
 func handleConn(conn net.Conn) {
   Defer Conn. Close ()// Close after completion
   spew.Dump(conn)
   Io. WriteString (conn, "Input Number:")
   scanner := bufio.NewScanner(conn)

   go func() {
      Scan () {// polling scans all TCP connections
         data, err := strconv.Atoi(scanner.Text())

         if err != nil {
            Log. Printf ("% V is not numeric", scanner. Text (), err)
         }
         newBlock, err := generateBlock(BlockChain[len(BlockChain) - 1], data)

         if err != nil {
            log.Println(err)
            continue
         }

         if isBlockValid(newBlock, BlockChain[len(BlockChain) - 1]) {
            newBlockChain := append(BlockChain, newBlock)
            replaceChain(newBlockChain)
         }

         BcServer < - BlockChain // Hands over the generated block data to the channel, one-way transfer
         Io. WriteString (conn, " n Input Number:")
      }
   }()

   go func() {
      For {// Synchronize every 10s
         time.Sleep(10 * time.Second)
         output, err := json.MarshalIndent(BlockChain, "", " ")

         if err != nil {
            log.Fatal(err)
         }

         io.WriteString(conn, "\n↓↓↓↓↓↓↓↓↓↓↓↓↓ 同步区块链:↓↓↓↓↓↓↓↓↓↓↓↓↓↓\n"+ string(output) + "\n↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\n")
      }
   }()

   for _= range bcServer {
      spew.Dump(BlockChain)
   }
 }

Principal function

func main () {
   err := godotenv.Load()
   if err != nil {
      log.Fatal(err)
}

BcServer = make (chan [] Block) // Create channels

t := time.Now()
genesisBlock := Block{0, t.String(), 0, "", "", difficulty, ""}
spew.Dump(genesisBlock)
BlockChain = append (BlockChain, genesis Block) // Genesis Block

Server, err: = net.Listen ("tcp", ":"+os.Getenv ("PORT")//Listen on TCP ports
if err != nil {
   log.Fatal(err)
}
Defer server. Close ()// Close server after completion

for {
   conn, err := server.Accept()
   if err != nil {
      log.Fatal()
   }
   Go handleConn (conn)//Co-Processing Connections
}
}

Full code

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "time"
    "os"
    "log"
    "net/http"
    "github.com/gorilla/mux"
    "encoding/json"
    "io"
    "github.com/davecgh/go-spew/spew"
    "sync"
    "strconv"
    "strings"
    "fmt"
    "github.com/joho/godotenv"
    "net"
    "bufio"
)

//////////////////// Processing block chain////////////////////
Const difficulty = 1 // Definition difficulty, which is the prefix of how many zeros a hash contains
type Block struct {
    Index int// indicates the location of the block chain in which the block is located
    Timestamp string // timestamp for generating blocks
    Data int // Write block data
    Hash string // hash of whole block data SHA256
    PrevHash string // Hash value of the previous block
    Difficulty int//Definition difficulty
    Nonce string // Define a Nonce
}
Var mutex = & sync.Mutex {}/// Prevents errors caused by concurrent write requests and adds mutex
Var BlockChain [] Block // Define a block chain with all data elements blocked
Var bcServer Chan [] Block // Defines a channel to handle synchronization between nodes
/**
Calculating block hash values
 */
func calculateHash(block Block) string {
    Record: = strconv. Itoa (block. Index) + block. Timestamp + strconv. Itoa (block. Data) + block. PrevHash + block. Nonce /// / Gets the string stitching of the current block block and records it according to the index, timestamp, data contained, and hash of the previous block. Nonce value is added together.
    H: = sha256. New ()// Get sha256 hash algorithm
    H.Write ([] byte (record)// Gets the corresponding hash
    hashed := h.Sum(nil)
    Return hex. EncodeToString (hashed) // Convert to String Return
}
/**
Generate a block based on the previous block
 */
func generateBlock(oldBlock Block, Data int) (Block, error) {
    var newBlock Block
    t := time.Now()
    New Block. Index = oldBlock. Index + 1 // Index Self-increasing
    NewBlock. Timestamp = t. String ()// timestamp
    New Block. Data = Data // Data
    New Block. PrevHash = oldBlock. Hash /// Hash of the previous block
    New Block. Difficulty = difficulty // difficulty
    // New Block. Hash = calculateHash (new Block) // Calculate the hash of this block
    for i := 0; ; i++ {
        Hex: = fmt. Sprintf ("% x", i) // hexadecimal display
        newBlock.Nonce = hex
        NewHash: = calculateHash (newBlock) // Computational Hash
        if !isHashValid(newHash, newBlock.Difficulty) {
            Fmt. Println (new Hash), "Keep trying! (")"
            Time. Sleep (time. Second) // Executed every 1s
            continue
        } else {
            Fmt. Println (new Hash): "Successful! ""
            newBlock.Hash = newHash
            break
        }
    }
    return newBlock, nil
}

/**
Verify whether the block is legal
 */
func isBlockValid(newBlock, oldBlock Block) bool {
    If oldBlock. Index + 1!= newBlock. Index {/// If the index does not inherit from the previous one, the validation does not pass.
        return false
    }
    If oldBlock. Hash!= newBlock. PrevHash {// If hash does not inherit the previous block, validation does not pass
        return false
    }
    If calculateHash (new Block)!= new Block. Hash {/// If the calculated hash is inconsistent, the verification does not pass.
        return false
    }
    return true
}
/**
Verify that the prefix of the hash contains difficulty 0
 */
func isHashValid(hash string, difficulty int) bool {
    prefix := strings.Repeat("0", difficulty)
    return strings.HasPrefix(hash, prefix)
}

/**
Choose the long chain as the correct chain
 */
 func replaceChain(newBlocks []Block) {
     If len (new Blocks) > len (BlockChain) {// Calculate array length
         BlockChain = newBlocks
    }
 }

 ////////////////// Principal function/////////////////
 
 func main () {
     err := godotenv.Load()
     if err != nil {
         log.Fatal(err)
    }

    BcServer = make (chan [] Block) // Create channels

    t := time.Now()
    genesisBlock := Block{0, t.String(), 0, "", "", difficulty, ""}
    spew.Dump(genesisBlock)
    BlockChain = append (BlockChain, genesis Block) // Genesis Block

    Server, err: = net.Listen ("tcp", ":"+os.Getenv ("PORT")//Listen on TCP ports
    if err != nil {
        log.Fatal(err)
    }
    Defer server. Close ()// Close server after completion

    for {
        conn, err := server.Accept()
        if err != nil {
            log.Fatal()
        }
        Go handleConn (conn)//Co-Processing Connections
    }
 }
/**
Processing connections
 */
 func handleConn(conn net.Conn) {
     Defer Conn. Close ()// Close after completion
    spew.Dump(conn)
     Io. WriteString (conn, "Input Number:")
     scanner := bufio.NewScanner(conn)

     go func() {
         Scan () {// polling scans all TCP connections
             data, err := strconv.Atoi(scanner.Text())

             if err != nil {
                 Log. Printf ("% V is not numeric", scanner. Text (), err)
            }
            newBlock, err := generateBlock(BlockChain[len(BlockChain) - 1], data)

            if err != nil {
                log.Println(err)
                continue
            }

            if isBlockValid(newBlock, BlockChain[len(BlockChain) - 1]) {
                newBlockChain := append(BlockChain, newBlock)
                replaceChain(newBlockChain)
            }

            BcServer < - BlockChain // Hands over the generated block data to the channel, one-way transfer
            Io. WriteString (conn, " n Input Number:")
        }
    }()

     go func() {
         For {// Synchronize every 10s
             time.Sleep(10 * time.Second)
             output, err := json.MarshalIndent(BlockChain, "", " ")

             if err != nil {
                 log.Fatal(err)
            }

            io.WriteString(conn, "\n↓↓↓↓↓↓↓↓↓↓↓↓↓ 同步区块链:↓↓↓↓↓↓↓↓↓↓↓↓↓↓\n"+ string(output) + "\n↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\n")
        }
    }()

     for _= range bcServer {
         spew.Dump(BlockChain)
    }
 }

Function

  • Open the terminal and run go run main. go as the main thread terminal
  • Open two new terminals as nodes, run NC localhost 8088 or telnet localhost 8088, enter the corresponding number
  • Waiting for the block to be generated, the main thread is shown as follows

Implementation of a Block Chain in Go Language

  • Each node receives synchronous block chain data from the main thread every 10 seconds

Implementation of a Block Chain in Go Language

  • You can change the value of the difficulty constant to 2 or 3, and the calculation time will multiply.

The broadcasting synchronization between the above nodes is achieved by TCP connection, but the better solution should be P2P network, which needs to install libp2p package, which is not discussed here.

Recommended Today

Implementation of PHP Facades

Example <?php class RealRoute{ public function get(){ Echo’Get me’; } } class Facade{ public static $resolvedInstance; public static $app; public static function __callStatic($method,$args){ $instance = static::getFacadeRoot(); if(!$instance){ throw new RuntimeException(‘A facade root has not been set.’); } return $instance->$method(…$args); } // Get the Facade root object public static function getFacadeRoot() { return static::resolveFacadeInstance(static::getFacadeAccessor()); } protected […]