区块链编程-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