区块链编程-golang(三)

 

原文:区块链实现(三)—用数据库存储区块信息  https://zhuanlan.zhihu.com/p/714854444

文件目录

主文件下有 文件blockchain、go.mod、gosum、文件tmp、main.go

文件blockhain下面有block.go、blockchain.go、proof.go

 

part1:block.go

package blockchain

import (
    "bytes"
    "encoding/gob"
    "log"
)

type Block struct {
    Hash     []byte
    Data     []byte
    PrevHash []byte
    Nonce    int
}

func CreateBlock(data string, prevHash []byte) *Block {
    block := &Block{[]byte{}, []byte(data), prevHash, 0}
    pow := NewProof(block)
    nonce, hash := pow.Run()
    block.Hash = hash[:]
    block.Nonce = nonce
    return block
}

func Genesis() *Block {
    return CreateBlock("Genesis", []byte{})
}

func (b *Block) Serialize() []byte {
    var res bytes.Buffer
    encoder := gob.NewEncoder(&res)
    err := encoder.Encode(b)
    Handle(err)
    return res.Bytes()
}

func Deserialize(data []byte) *Block {
    var block Block
    decoder := gob.NewDecoder(bytes.NewReader(data))
    err := decoder.Decode(&block)
    Handle(err)
    return &block
}

func Handle(err error) {
    if err != nil {
        log.Panic(err)
    }
}

 

part2 : blockchain.go

package blockchain

import (
    "fmt"

    "github.com/dgraph-io/badger"
)

const (
    dbPath = "./tmp/blocks"
)

type BlockChain struct {
    LastHash []byte
    Database *badger.DB
}

type BlockChainIterator struct {
    CurrentHash []byte
    Database    *badger.DB
}

func InitBlockChain() *BlockChain {

    // 存储最新的区块哈希
    var lastHash []byte

    opts := badger.DefaultOptions
    opts.Dir = dbPath
    opts.ValueDir = dbPath

    db, err := badger.Open(opts)
    Handle(err)

    err = db.Update(func(txn *badger.Txn) error {
        if _, err := txn.Get([]byte("lh")); err == badger.ErrKeyNotFound {
            fmt.Println("No existing blockchain found")
            genesis := Genesis()
            fmt.Println("Genesis proved")
            //存储创世区块
            err = txn.Set(genesis.Hash, genesis.Serialize())
            Handle(err)
            //存储创世区块链哈希
            err = txn.Set([]byte("lh"), genesis.Hash)

            lastHash = genesis.Hash

            return err
        } else {
            item, err := txn.Get([]byte("lh"))
            Handle(err)
            lastHash, err = item.Value()
            return err
        }
    })

    Handle(err)

    blockchain := BlockChain{lastHash, db}
    return &blockchain
}

func (chain *BlockChain) AddBlock(data string) {
    var lastHash []byte

    err := chain.Database.View(func(txn *badger.Txn) error {
        // 获取最新哈希信息
        item, err := txn.Get([]byte("lh"))
        Handle(err)
        lastHash, err = item.Value()

        return err
    })
    Handle(err)

    // 将最新data和哈希信息获取新的区块
    newBlock := CreateBlock(data, lastHash)

    err = chain.Database.Update(func(txn *badger.Txn) error {
        // 保存序列化信息
        err := txn.Set(newBlock.Hash, newBlock.Serialize())
        Handle(err)
        err = txn.Set([]byte("lh"), newBlock.Hash)

        chain.LastHash = newBlock.Hash

        return err
    })
    Handle(err)
}

func (chain *BlockChain) Iterator() *BlockChainIterator {
    iter := &BlockChainIterator{chain.LastHash, chain.Database}

    return iter
}

func (iter *BlockChainIterator) Next() *Block {
    var block *Block

    err := iter.Database.View(func(txn *badger.Txn) error {
        item, err := txn.Get(iter.CurrentHash)
        Handle(err)
        encodedBlock, err := item.Value()
        block = Deserialize(encodedBlock)

        return err
    })
    Handle(err)

    iter.CurrentHash = block.PrevHash

    return block
}

proof.go

package blockchain

import (
    "bytes"
    "crypto/sha256"
    "encoding/binary"
    "fmt"
    "log"
    "math"
    "math/big"
)

// Take the data from the block

// create a counter (nonce) which starts at 0

// create a hash of the data plus the counter

// check the hash to see if it meets a set of requirements

// Requirements:
// The First few bytes must contain 0s

const Difficulty = 18

type ProofOfWork struct {
    Block  *Block
    Target *big.Int
}

func NewProof(b *Block) *ProofOfWork {
    target := big.NewInt(1)
    target.Lsh(target, uint(256-Difficulty))

    pow := &ProofOfWork{b, target}

    return pow
}

func (pow *ProofOfWork) InitData(nonce int) []byte {
    data := bytes.Join(
        [][]byte{
            pow.Block.PrevHash,
            pow.Block.Data,
            ToHex(int64(nonce)),
            ToHex(int64(Difficulty)),
        },
        []byte{},
    )

    return data
}

func (pow *ProofOfWork) Run() (int, []byte) {
    var intHash big.Int
    var hash [32]byte

    nonce := 0

    for nonce < math.MaxInt64 {
        data := pow.InitData(nonce)
        hash = sha256.Sum256(data)

        fmt.Printf("\r%x", hash)
        intHash.SetBytes(hash[:])

        if intHash.Cmp(pow.Target) == -1 {
            break
        } else {
            nonce++
        }

    }
    fmt.Println()

    return nonce, hash[:]
}

func (pow *ProofOfWork) Validate() bool {
    var intHash big.Int

    data := pow.InitData(pow.Block.Nonce)

    hash := sha256.Sum256(data)
    intHash.SetBytes(hash[:])

    return intHash.Cmp(pow.Target) == -1
}

func ToHex(num int64) []byte {
    buff := new(bytes.Buffer)
    err := binary.Write(buff, binary.BigEndian, num)
    if err != nil {
        log.Panic(err)

    }

    return buff.Bytes()
}

 

 

part4: go.mod

module github.com/tensor-programming/golang-blockchain

require (
    github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 // indirect
    github.com/dgraph-io/badger v1.5.4
    github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 // indirect
    github.com/golang/protobuf v1.2.0 // indirect
    github.com/pkg/errors v0.8.0 // indirect
    golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect
)

 

part5: go.sum

github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg=
github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+34X//E8Wg3nEbMJxJkwSc0tPePK0=
github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

 

part6 : main.go

package main

import (
    "flag"
    "fmt"
    "os"
    "runtime"
    "strconv"

    "github.com/tensor-programming/golang-blockchain/blockchain"
)

type CommandLine struct {
    blockchain *blockchain.BlockChain
}

func (cli *CommandLine) printUsage() {
    fmt.Println("Usage:")
    fmt.Println(" add -block BLOCK_DATA - add a block to the chain")
    fmt.Println(" print - Prints the blocks in the chain")
}

func (cli *CommandLine) validateArgs() {
    if len(os.Args) < 2 {
        cli.printUsage()
        runtime.Goexit()
    }
}

func (cli *CommandLine) addBlock(data string) {
    cli.blockchain.AddBlock(data)
    fmt.Println("Added Block!")
}

func (cli *CommandLine) printChain() {
    iter := cli.blockchain.Iterator()

    for {
        block := iter.Next()

        fmt.Printf("Prev. hash: %x\n", block.PrevHash)
        fmt.Printf("Data: %s\n", block.Data)
        fmt.Printf("Hash: %x\n", block.Hash)
        pow := blockchain.NewProof(block)
        fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate()))
        fmt.Println()

        if len(block.PrevHash) == 0 {
            break
        }
    }
}

func (cli *CommandLine) run() {
    cli.validateArgs()

    addBlockCmd := flag.NewFlagSet("add", flag.ExitOnError)
    printChainCmd := flag.NewFlagSet("print", flag.ExitOnError)
    addBlockData := addBlockCmd.String("block", "", "Block data")

    switch os.Args[1] {
    case "add":
        err := addBlockCmd.Parse(os.Args[2:])
        blockchain.Handle(err)

    case "print":
        err := printChainCmd.Parse(os.Args[2:])
        blockchain.Handle(err)

    default:
        cli.printUsage()
        runtime.Goexit()
    }

    if addBlockCmd.Parsed() {
        if *addBlockData == "" {
            addBlockCmd.Usage()
            runtime.Goexit()
        }
        cli.addBlock(*addBlockData)
    }

    if printChainCmd.Parsed() {
        cli.printChain()
    }
}

func main() {
    //运行数据库
    defer os.Exit(0)

    // 获取最新的块信息
    chain := blockchain.InitBlockChain()

    //关闭数据库
    defer chain.Database.Close()

    cli := CommandLine{chain}
    cli.run()
}

 

运行创世块 go run main.go

2024/08/16 10:28:55 Replaying from value pointer: {Fid:0 Len:0 Offset:0}
2024/08/16 10:28:55 Iterating file id: 0
2024/08/16 10:28:55 Iteration took: 0s
No existing blockchain found
0000342dc11a9fd1833ed9fe18ca5627cedc56507de6698acfcafd301398cb35
Genesis proved

 

 

运行 添加第一个块 : go run main.go add -block "first block"   

2024/08/16 11:53:01 Replaying from value pointer: {Fid:0 Len:42 Offset:238}
2024/08/16 11:53:01 Iterating file id: 0
2024/08/16 11:53:01 Iteration took: 202.3µs
Added Block!

 

运行 添加第二个块 : go run main.go add -block "second block"   

2024/08/16 13:55:43 Replaying from value pointer: {Fid:0 Len:42 Offset:556}
2024/08/16 13:55:43 Iterating file id: 0
2024/08/16 13:55:43 Iteration took: 0s
000021de42633e4a04f12b135d0f96e5682e27f5f317439de74c6690adf04d92
Added Block!

 

运行 添加第三个块(fourth 是 第四个这里写错了) :go run main.go add -block "fourth block"

2024/08/16 15:15:21 Replaying from value pointer: {Fid:0 Len:42 Offset:875}
2024/08/16 15:15:21 Iterating file id: 0
2024/08/16 15:15:21 Iteration took: 0s
00000336d28b79a0fb309843141896107346f78165635b6e64c84efe3a670b0d
Added Block!

 

 

打印所有块 go run main.go print

2024/08/16 15:25:26 Replaying from value pointer: {Fid:0 Len:42 Offset:1194}
2024/08/16 15:25:26 Iterating file id: 0
2024/08/16 15:25:26 Iteration took: 0s
Prev. hash: 000021de42633e4a04f12b135d0f96e5682e27f5f317439de74c6690adf04d92
Data: fourth block
Hash: 00000336d28b79a0fb309843141896107346f78165635b6e64c84efe3a670b0d
PoW: true

Prev. hash: 00002c3afdd8f7ee21f4dc366c79e3408da23f583847e19bbdc5a25b7e151560
Data: second block
Hash: 000021de42633e4a04f12b135d0f96e5682e27f5f317439de74c6690adf04d92
PoW: true

Prev. hash: 0000342dc11a9fd1833ed9fe18ca5627cedc56507de6698acfcafd301398cb35
Data: first block
Hash: 00002c3afdd8f7ee21f4dc366c79e3408da23f583847e19bbdc5a25b7e151560
PoW: true

Prev. hash:
Data: Genesis
Hash: 0000342dc11a9fd1833ed9fe18ca5627cedc56507de6698acfcafd301398cb35
PoW: true

 

posted @ 2024-08-16 15:29  apeNote  阅读(3)  评论(0编辑  收藏  举报